<commit_before>/*
*
* Copyright 2015 Telefonica Investigacion y Desarrollo, S.A.U
*
* This file is part of Orion Context Broker.
*
* Orion Context Broker is free software: you can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* Orion Context Broker is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero
* General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Orion Context Broker. If not, see http://www.gnu.org/licenses/.
*
* For those usages not covered by this license please contact with
* iot_support at tid dot es
*
* Author: Orion dev team
*/

#include <algorithm>

#include "rapidjson/document.h"

#include "alarmMgr/alarmMgr.h"
#include "common/globals.h"
#include "common/RenderFormat.h"
#include "common/string.h"
#include "rest/ConnectionInfo.h"
#include "rest/OrionError.h"
#include "ngsi/Request.h"
#include "jsonParseV2/jsonParseTypeNames.h"
#include "jsonParseV2/jsonRequestTreat.h"
#include "jsonParseV2/utilsParse.h"
#include "rest/Verb.h"

#include "jsonParseV2/parseSubscription.h"

using namespace rapidjson;

using ngsiv2::SubscriptionUpdate;
using ngsiv2::EntID;

/* Prototypes */
static std::string parseAttributeList(ConnectionInfo* ciP, std::vector<std::string>* vec, const Value& attributes);
static std::string parseNotification(ConnectionInfo* ciP, SubscriptionUpdate* subsP, const Value& notification);
static std::string parseSubject(ConnectionInfo* ciP, SubscriptionUpdate* subsP, const Value& subject);
static std::string parseEntitiesVector(ConnectionInfo* ciP, std::vector<EntID>* eivP, const Value& entities);
static std::string parseNotifyConditionVector(ConnectionInfo* ciP, SubscriptionUpdate* subsP, const Value& condition);
static std::string error(ConnectionInfo* ciP, const std::string& msg);

/* ****************************************************************************
*
* parseSubscription -
*
*/
std::string parseSubscription(ConnectionInfo* ciP, SubscriptionUpdate* subsP, bool update)
{
  Document document;

  document.Parse(ciP->payload);

  if (document.HasParseError())
  {
    OrionError oe(SccBadRequest, "Errors found in incoming JSON buffer", ERROR_STRING_PARSERROR);
    alarmMgr.badInput(clientIp, "JSON parse error");
    return oe.render(ciP, "");
  }

  if (!document.IsObject())
  {
    OrionError oe(SccBadRequest, "Error parsing incoming JSON buffer", ERROR_STRING_PARSERROR);
    alarmMgr.badInput(clientIp, "JSON parse error");
    return oe.render(ciP, "");
  }

  if (document.ObjectEmpty())
  {
    //
    // Initially we used the method "Empty". As the broker crashed inside that method, some
    // research was made and "ObjectEmpty" was found. As the broker stopped crashing and complaints
    // about crashes with small docs and "Empty()" were found on the internet, we opted to use ObjectEmpty
    //
    return error(ciP, "empty payload");
  }

  // Description field

  Opt<std::string> description = getStringOpt(document, "description");
  if (!description.ok())
  {
    return description.error;
  }
  else if (description.given)
  {
    std::string descriptionString = description.value;

    if (descriptionString.length() > MAX_DESCRIPTION_LENGTH)
    {
      return error(ciP, "max description length exceeded");
    }

    subsP->descriptionProvided = true;
    subsP->description = descriptionString;
  }

  // Subject field
  if (document.HasMember("subject"))
  {
    const Value&  subject = document["subject"];
    std::string   r       = parseSubject(ciP, subsP, subject);

    if (r != "")
    {
      return r;
    }
  }
  else if (!update)
  {
    return error(ciP, "no subject for subscription specified");
  }

  // Notification field
  if (document.HasMember("notification"))
  {
    const Value&  notification = document["notification"];
    std::string   r            = parseNotification(ciP, subsP, notification);

    if (r != "")
    {
      return r;
    }
  }
  else if (!update)
  {
    return error(ciP, "no notification for subscription specified");
  }

  // Expires field
  Opt<std::string> expiresOpt = getStringOpt(document, "expires");

  if (!expiresOpt.ok())
  {
    return expiresOpt.error;
  }
  else if (expiresOpt.given)
  {
    std::string expires = expiresOpt.value;

    int64_t eT = -1;

    if (expires.empty())
    {
        eT = PERMANENT_SUBS_DATETIME;
    }
    else
    {
      eT = parse8601Time(expires);
      if (eT == -1)
      {
        return error(ciP, "expires has an invalid format");
      }
    }

    subsP->expiresProvided = true;
    subsP->expires = eT;
  }
  else if (!update)
  {
    subsP->expires = PERMANENT_SUBS_DATETIME;
  }

  // Status field
  Opt<std::string> statusOpt =  getStringOpt(document, "status");
  if (!statusOpt.ok())
  {
    return statusOpt.error;
  }
  else if (statusOpt.given)
  {
    std::string statusString = statusOpt.value;

    if ((statusString != "active") && (statusString != "inactive"))
    {
      return error(ciP, "status is not valid (it has to be either active or inactive)");
    }
    subsP->statusProvided = true;
    subsP->status = statusString;
  }

  // Throttling
  Opt<int64_t> throttlingOpt = getInt64Opt(document, "throttling");
  if (!throttlingOpt.ok())
  {
    return throttlingOpt.error;
  }
  else if (throttlingOpt.given)
  {
    subsP->throttlingProvided = true;
    subsP->throttling = throttlingOpt.value;
  }
  else if (!update) // throttling was not set and it is not update
  {
    subsP->throttling = 0; // Default value if not provided at creation => no throttling
  }

  // attrsFormat field
  Opt<std::string>  attrsFormatOpt = getStringOpt(document, "attrsFormat");
  if (!attrsFormatOpt.ok())
  {
    return attrsFormatOpt.error;
  }
  else if (attrsFormatOpt.given)
  {
    std::string   attrsFormatString = attrsFormatOpt.value;
    RenderFormat  nFormat           = stringToRenderFormat(attrsFormatString, true);

    if (nFormat == NO_FORMAT)
    {
      return error(ciP, "invalid attrsFormat (accepted values: legacy, normalized, keyValues, values)");
    }
    subsP->attrsFormatProvided = true;
    subsP->attrsFormat = nFormat;
  }
  else if (!update) // Default value for creation
  {
    subsP->attrsFormat = DEFAULT_RENDER_FORMAT;  // Default format for NGSIv2: normalized
  }

  return "OK";
}

/* ****************************************************************************
*
* parseSubject -
*
*/
static std::string parseSubject(ConnectionInfo* ciP, SubscriptionUpdate* subsP, const Value& subject)
{
  std::string r;

  subsP->subjectProvided  = true;

  if (!subject.IsObject())
  {
    return error(ciP, "subject is not an object");
  }

  // Entities
  if (!subject.HasMember("entities"))
  {
    return error(ciP, "no subject entities specified");
  }

  r = parseEntitiesVector(ciP, &subsP->subject.entities, subject["entities"] );
  if (r != "")
  {
    return r;
  }

  // Condition
  if (!subject.HasMember("condition"))
  {
    return error(ciP, "no subject condition specified");
  }

  const Value& condition = subject["condition"];
  if (!condition.IsObject())
  {
    return error(ciP, "condition is not an object");
  }
  r = parseNotifyConditionVector(ciP, subsP, condition);

  return r;
}

/* ****************************************************************************
*
* parseEntitiesVector -
*
*/
static std::string parseEntitiesVector(ConnectionInfo* ciP, std::vector<EntID>* eivP, const Value& entities)
{
  if (!entities.IsArray())
  {
    return error(ciP, "subject entities is not an array");
  }

  for (Value::ConstValueIterator iter = entities.Begin(); iter != entities.End(); ++iter)
  {
    if (!iter->IsObject())
    {
     return error(ciP, "subject entities element is not an object");
    }
    if (!iter->HasMember("id") && !iter->HasMember("idPattern") && !iter->HasMember("type"))
    {
      return error(ciP, "subject entities element has not id/idPattern nor type");
    }

    if (iter->HasMember("id") && iter->HasMember("idPattern"))
    {
      return error(ciP, "subject entities element has id and idPattern");
    }

    std::string  id;
    std::string  type;
    std::string  isPattern  = "false";

    Opt<std::string> idOpt = getStringOpt(*iter,"id", "subject entities element id");
    if (!idOpt.ok())
    {
      return idOpt.error;
    }
    id = idOpt.value;

    Opt<std::string> idPatOpt = getStringOpt(*iter,"idPattern", "subject entities element idPattern");
    if (!idPatOpt.ok())
    {
      return idOpt.error;
    }
    id = idPatOpt.value;
    isPattern = "true";

    if (id.empty())  // Only type was provided
    {
      id        = ".*";
      isPattern = "true";
    }

    Opt<std::string> typeOpt = getStringOpt(*iter, "type", "subject entities element type");
    if (!typeOpt.ok())
    {
      return typeOpt.error;
    }
    if (typeOpt.given)
    {
      type = typeOpt.value;
    }

    EntID  eid(id, isPattern, type);

    if (std::find(eivP->begin(), eivP->end(), eid) == eivP->end()) // if not already included
    {
      eivP->push_back(eid);
    }
  }

  return "";
}

/* ****************************************************************************
*
* parseNotification -
*
*/
static std::string parseNotification(ConnectionInfo* ciP, SubscriptionUpdate* subsP, const Value& notification)
{

  subsP->notificationProvided = true;

  if (!notification.IsObject())
  {
    return error(ciP, "notitication is not an object");
  }

/*
 *
 * XOR de http/httpExtended. En funcion de como se defina
 *  Ver si tiene sentido chequear la URL, si es que se puede
 * templatizar en la llamada tradicional
 *
 *
 */

  // Callback
  if (notification.HasMember("http"))
  {
    const Value& http = notification["http"];
    if (!http.IsObject())
    {
      return error(ciP, "http notitication is not an object");
    }

    // http - url
    std::string url = getString(http, "url", "url http notification");

    {
       std::string  host;
       int          port;
       std::string  path;
       std::string  protocol;

       if (parseUrl(url, host, port, path, protocol) == false)
       {
          return error(ciP, "Invalid URL parsing notification url");
       }
    }

    subsP->notification.httpInfo.url =      url;
    subsP->notification.httpInfo.extended = false;

  }
  else if (notification.HasMember("httpExtended"))
  {
    const Value& httpExt = notification["httpExtended"];
    if (!httpExt.IsObject())
    {
      return error(ciP, "httpExtended notitication is not an object");
    }

    // URL
    std::string url = getString(httpExt, "url", "url http notification");

    subsP->notification.httpInfo.url = url;

    // method -> verb
    std::string method = getString(httpExt, "method", "method http notification");
    Verb  verb         = str2Verb(method);
    /*
     *  CHECK IT IS VALID
     */

    subsP->notification.httpInfo.verb = verb;

    // payload
    std::string payload = getString(httpExt, "payload", "payload http notification");
    subsP->notification.httpInfo.payload = payload;

    subsP->notification.httpInfo.extended = true;

  }
  else  // missing callback field
  {
    return error(ciP, "http notification is missing");
  }

  // Attributes
  if (notification.HasMember("attrs"))
  {
    std::string r = parseAttributeList(ciP, &subsP->notification.attributes, notification["attrs"]);

    if (r != "")
    {
      return r;
    }
  }

   return "";
}

/* ****************************************************************************
*
* parseNotifyConditionVector -
*
*/
static std::string parseNotifyConditionVector(ConnectionInfo* ciP, ngsiv2::SubscriptionUpdate* subsP, const Value& condition)
{

  if (!condition.IsObject())
  {
   return error(ciP, "condition is not an object");
  }
  // Attributes
  if (!condition.HasMember("attrs"))
  {
    return error(ciP, "no condition attrs specified");
  }

  std::string r = parseAttributeList(ciP, &subsP->subject.condition.attributes, condition["attrs"]);

  if (r != "")
  {
    return r;
  }

  if (condition.HasMember("expression"))
  {
    const Value& expression = condition["expression"];

    if (!expression.IsObject())
    {
      return error(ciP, "expression is not an object");
    }

    subsP->subject.condition.expression.isSet = true;

    if (expression.HasMember("q"))
    {
      const Value& q = expression["q"];
      if (!q.IsString())
      {
        return error(ciP, "q is not a string");
      }
      subsP->subject.condition.expression.q = q.GetString();

      std::string  errorString;
      Scope*       scopeP = new Scope(SCOPE_TYPE_SIMPLE_QUERY, expression["q"].GetString());

      scopeP->stringFilterP = new StringFilter();
      if (scopeP->stringFilterP->parse(scopeP->value.c_str(), &errorString) == false)
      {
        delete scopeP->stringFilterP;
        delete scopeP;

        return error(ciP, errorString);

      }

      subsP->restriction.scopeVector.push_back(scopeP);
    }

    // geometry
    Opt<std::string> geometryOpt = getStringOpt(expression, "geometry");

    if (!geometryOpt.ok())
    {
      return geometryOpt.error;
    }
    else if (geometryOpt.given)
    {
      subsP->subject.condition.expression.geometry = geometryOpt.value;
    }

    // coords
    Opt<std::string> coordsOpt = getStringOpt(expression, "coords");

    if (!coordsOpt.ok())
    {
        return coordsOpt.error;
    }
    else if (coordsOpt.given)
    {
      subsP->subject.condition.expression.coords = coordsOpt.value;
    }

    // georel
    Opt<std::string> georelOpt = getStringOpt(expression, "georel");
    if (!geometryOpt.ok())
    {
      return geometryOpt.error;
    }
    if (georelOpt.given)
    {
      subsP->subject.condition.expression.georel = georelOpt.value;
    }
  }
  return "";
}

/* ****************************************************************************
*
* parseAttributeList -
*
*/
static std::string parseAttributeList(ConnectionInfo* ciP, std::vector<std::string>* vec, const Value& attributes)
{
  if (!attributes.IsArray())
  {
    return error(ciP, "attrs is not an array");
  }

  for (Value::ConstValueIterator iter = attributes.Begin(); iter != attributes.End(); ++iter)
  {
    if (!iter->IsString())
    {
      return error(ciP, "attrs element is not an string");
    }

    vec->push_back(iter->GetString());
  }

  return "";
}

/* ****************************************************************************
*
* error -
*
*/
static std::string error(ConnectionInfo* ciP, const std::string& msg)
{
  alarmMgr.badInput(clientIp, msg);
  OrionError oe(SccBadRequest, msg);

  return oe.render(ciP, "");
}

<commit_msg>Do not use isPattern<commit_after>/*
*
* Copyright 2015 Telefonica Investigacion y Desarrollo, S.A.U
*
* This file is part of Orion Context Broker.
*
* Orion Context Broker is free software: you can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* Orion Context Broker is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero
* General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Orion Context Broker. If not, see http://www.gnu.org/licenses/.
*
* For those usages not covered by this license please contact with
* iot_support at tid dot es
*
* Author: Orion dev team
*/

#include <algorithm>

#include "rapidjson/document.h"

#include "alarmMgr/alarmMgr.h"
#include "common/globals.h"
#include "common/RenderFormat.h"
#include "common/string.h"
#include "rest/ConnectionInfo.h"
#include "rest/OrionError.h"
#include "ngsi/Request.h"
#include "jsonParseV2/jsonParseTypeNames.h"
#include "jsonParseV2/jsonRequestTreat.h"
#include "jsonParseV2/utilsParse.h"
#include "rest/Verb.h"

#include "jsonParseV2/parseSubscription.h"

using namespace rapidjson;

using ngsiv2::SubscriptionUpdate;
using ngsiv2::EntID;

/* Prototypes */
static std::string parseAttributeList(ConnectionInfo* ciP, std::vector<std::string>* vec, const Value& attributes);
static std::string parseNotification(ConnectionInfo* ciP, SubscriptionUpdate* subsP, const Value& notification);
static std::string parseSubject(ConnectionInfo* ciP, SubscriptionUpdate* subsP, const Value& subject);
static std::string parseEntitiesVector(ConnectionInfo* ciP, std::vector<EntID>* eivP, const Value& entities);
static std::string parseNotifyConditionVector(ConnectionInfo* ciP, SubscriptionUpdate* subsP, const Value& condition);
static std::string error(ConnectionInfo* ciP, const std::string& msg);

/* ****************************************************************************
*
* parseSubscription -
*
*/
std::string parseSubscription(ConnectionInfo* ciP, SubscriptionUpdate* subsP, bool update)
{
  Document document;

  document.Parse(ciP->payload);

  if (document.HasParseError())
  {
    OrionError oe(SccBadRequest, "Errors found in incoming JSON buffer", ERROR_STRING_PARSERROR);
    alarmMgr.badInput(clientIp, "JSON parse error");
    return oe.render(ciP, "");
  }

  if (!document.IsObject())
  {
    OrionError oe(SccBadRequest, "Error parsing incoming JSON buffer", ERROR_STRING_PARSERROR);
    alarmMgr.badInput(clientIp, "JSON parse error");
    return oe.render(ciP, "");
  }

  if (document.ObjectEmpty())
  {
    //
    // Initially we used the method "Empty". As the broker crashed inside that method, some
    // research was made and "ObjectEmpty" was found. As the broker stopped crashing and complaints
    // about crashes with small docs and "Empty()" were found on the internet, we opted to use ObjectEmpty
    //
    return error(ciP, "empty payload");
  }

  // Description field

  Opt<std::string> description = getStringOpt(document, "description");
  if (!description.ok())
  {
    return description.error;
  }
  else if (description.given)
  {
    std::string descriptionString = description.value;

    if (descriptionString.length() > MAX_DESCRIPTION_LENGTH)
    {
      return error(ciP, "max description length exceeded");
    }

    subsP->descriptionProvided = true;
    subsP->description = descriptionString;
  }

  // Subject field
  if (document.HasMember("subject"))
  {
    const Value&  subject = document["subject"];
    std::string   r       = parseSubject(ciP, subsP, subject);

    if (r != "")
    {
      return r;
    }
  }
  else if (!update)
  {
    return error(ciP, "no subject for subscription specified");
  }

  // Notification field
  if (document.HasMember("notification"))
  {
    const Value&  notification = document["notification"];
    std::string   r            = parseNotification(ciP, subsP, notification);

    if (r != "")
    {
      return r;
    }
  }
  else if (!update)
  {
    return error(ciP, "no notification for subscription specified");
  }

  // Expires field
  Opt<std::string> expiresOpt = getStringOpt(document, "expires");

  if (!expiresOpt.ok())
  {
    return expiresOpt.error;
  }
  else if (expiresOpt.given)
  {
    std::string expires = expiresOpt.value;

    int64_t eT = -1;

    if (expires.empty())
    {
        eT = PERMANENT_SUBS_DATETIME;
    }
    else
    {
      eT = parse8601Time(expires);
      if (eT == -1)
      {
        return error(ciP, "expires has an invalid format");
      }
    }

    subsP->expiresProvided = true;
    subsP->expires = eT;
  }
  else if (!update)
  {
    subsP->expires = PERMANENT_SUBS_DATETIME;
  }

  // Status field
  Opt<std::string> statusOpt =  getStringOpt(document, "status");
  if (!statusOpt.ok())
  {
    return statusOpt.error;
  }
  else if (statusOpt.given)
  {
    std::string statusString = statusOpt.value;

    if ((statusString != "active") && (statusString != "inactive"))
    {
      return error(ciP, "status is not valid (it has to be either active or inactive)");
    }
    subsP->statusProvided = true;
    subsP->status = statusString;
  }

  // Throttling
  Opt<int64_t> throttlingOpt = getInt64Opt(document, "throttling");
  if (!throttlingOpt.ok())
  {
    return throttlingOpt.error;
  }
  else if (throttlingOpt.given)
  {
    subsP->throttlingProvided = true;
    subsP->throttling = throttlingOpt.value;
  }
  else if (!update) // throttling was not set and it is not update
  {
    subsP->throttling = 0; // Default value if not provided at creation => no throttling
  }

  // attrsFormat field
  Opt<std::string>  attrsFormatOpt = getStringOpt(document, "attrsFormat");
  if (!attrsFormatOpt.ok())
  {
    return attrsFormatOpt.error;
  }
  else if (attrsFormatOpt.given)
  {
    std::string   attrsFormatString = attrsFormatOpt.value;
    RenderFormat  nFormat           = stringToRenderFormat(attrsFormatString, true);

    if (nFormat == NO_FORMAT)
    {
      return error(ciP, "invalid attrsFormat (accepted values: legacy, normalized, keyValues, values)");
    }
    subsP->attrsFormatProvided = true;
    subsP->attrsFormat = nFormat;
  }
  else if (!update) // Default value for creation
  {
    subsP->attrsFormat = DEFAULT_RENDER_FORMAT;  // Default format for NGSIv2: normalized
  }

  return "OK";
}

/* ****************************************************************************
*
* parseSubject -
*
*/
static std::string parseSubject(ConnectionInfo* ciP, SubscriptionUpdate* subsP, const Value& subject)
{
  std::string r;

  subsP->subjectProvided  = true;

  if (!subject.IsObject())
  {
    return error(ciP, "subject is not an object");
  }

  // Entities
  if (!subject.HasMember("entities"))
  {
    return error(ciP, "no subject entities specified");
  }

  r = parseEntitiesVector(ciP, &subsP->subject.entities, subject["entities"] );
  if (r != "")
  {
    return r;
  }

  // Condition
  if (!subject.HasMember("condition"))
  {
    return error(ciP, "no subject condition specified");
  }

  const Value& condition = subject["condition"];
  if (!condition.IsObject())
  {
    return error(ciP, "condition is not an object");
  }
  r = parseNotifyConditionVector(ciP, subsP, condition);

  return r;
}

/* ****************************************************************************
*
* parseEntitiesVector -
*
*/
static std::string parseEntitiesVector(ConnectionInfo* ciP, std::vector<EntID>* eivP, const Value& entities)
{
  if (!entities.IsArray())
  {
    return error(ciP, "subject entities is not an array");
  }

  for (Value::ConstValueIterator iter = entities.Begin(); iter != entities.End(); ++iter)
  {
    if (!iter->IsObject())
    {
     return error(ciP, "subject entities element is not an object");
    }
    if (!iter->HasMember("id") && !iter->HasMember("idPattern") && !iter->HasMember("type"))
    {
      return error(ciP, "subject entities element has not id/idPattern nor type");
    }

    if (iter->HasMember("id") && iter->HasMember("idPattern"))
    {
      return error(ciP, "subject entities element has id and idPattern");
    }

    std::string  id;
    std::string  idPattern;
    std::string  type;

    Opt<std::string> idOpt = getStringOpt(*iter,"id", "subject entities element id");
    if (!idOpt.ok())
    {
      return idOpt.error;
    }
    id = idOpt.value;

    Opt<std::string> idPatOpt = getStringOpt(*iter,"idPattern", "subject entities element idPattern");
    if (!idPatOpt.ok())
    {
      return idOpt.error;
    }
    idPattern = idPatOpt.value;

    Opt<std::string> typeOpt = getStringOpt(*iter, "type", "subject entities element type");
    if (!typeOpt.ok())
    {
      return typeOpt.error;
    }
    if (typeOpt.given)
    {
      type = typeOpt.value;
    }

    EntID  eid(id, idPattern, type);

    if (std::find(eivP->begin(), eivP->end(), eid) == eivP->end()) // if not already included
    {
      eivP->push_back(eid);
    }
  }

  return "";
}

/* ****************************************************************************
*
* parseNotification -
*
*/
static std::string parseNotification(ConnectionInfo* ciP, SubscriptionUpdate* subsP, const Value& notification)
{

  subsP->notificationProvided = true;

  if (!notification.IsObject())
  {
    return error(ciP, "notitication is not an object");
  }

/*
 *
 * XOR de http/httpExtended. En funcion de como se defina
 *  Ver si tiene sentido chequear la URL, si es que se puede
 * templatizar en la llamada tradicional
 *
 *
 */

  // Callback
  if (notification.HasMember("http"))
  {
    const Value& http = notification["http"];
    if (!http.IsObject())
    {
      return error(ciP, "http notitication is not an object");
    }

    // http - url
    std::string url = getString(http, "url", "url http notification");

    {
       std::string  host;
       int          port;
       std::string  path;
       std::string  protocol;

       if (parseUrl(url, host, port, path, protocol) == false)
       {
          return error(ciP, "Invalid URL parsing notification url");
       }
    }

    subsP->notification.httpInfo.url =      url;
    subsP->notification.httpInfo.extended = false;

  }
  else if (notification.HasMember("httpExtended"))
  {
    const Value& httpExt = notification["httpExtended"];
    if (!httpExt.IsObject())
    {
      return error(ciP, "httpExtended notitication is not an object");
    }

    // URL
    std::string url = getString(httpExt, "url", "url http notification");

    subsP->notification.httpInfo.url = url;

    // method -> verb
    std::string method = getString(httpExt, "method", "method http notification");
    Verb  verb         = str2Verb(method);
    /*
     *  CHECK IT IS VALID
     */

    subsP->notification.httpInfo.verb = verb;

    // payload
    std::string payload = getString(httpExt, "payload", "payload http notification");
    subsP->notification.httpInfo.payload = payload;

    subsP->notification.httpInfo.extended = true;

  }
  else  // missing callback field
  {
    return error(ciP, "http notification is missing");
  }

  // Attributes
  if (notification.HasMember("attrs"))
  {
    std::string r = parseAttributeList(ciP, &subsP->notification.attributes, notification["attrs"]);

    if (r != "")
    {
      return r;
    }
  }

   return "";
}

/* ****************************************************************************
*
* parseNotifyConditionVector -
*
*/
static std::string parseNotifyConditionVector(ConnectionInfo* ciP, ngsiv2::SubscriptionUpdate* subsP, const Value& condition)
{

  if (!condition.IsObject())
  {
   return error(ciP, "condition is not an object");
  }
  // Attributes
  if (!condition.HasMember("attrs"))
  {
    return error(ciP, "no condition attrs specified");
  }

  std::string r = parseAttributeList(ciP, &subsP->subject.condition.attributes, condition["attrs"]);

  if (r != "")
  {
    return r;
  }

  if (condition.HasMember("expression"))
  {
    const Value& expression = condition["expression"];

    if (!expression.IsObject())
    {
      return error(ciP, "expression is not an object");
    }

    subsP->subject.condition.expression.isSet = true;

    if (expression.HasMember("q"))
    {
      const Value& q = expression["q"];
      if (!q.IsString())
      {
        return error(ciP, "q is not a string");
      }
      subsP->subject.condition.expression.q = q.GetString();

      std::string  errorString;
      Scope*       scopeP = new Scope(SCOPE_TYPE_SIMPLE_QUERY, expression["q"].GetString());

      scopeP->stringFilterP = new StringFilter();
      if (scopeP->stringFilterP->parse(scopeP->value.c_str(), &errorString) == false)
      {
        delete scopeP->stringFilterP;
        delete scopeP;

        return error(ciP, errorString);

      }

      subsP->restriction.scopeVector.push_back(scopeP);
    }

    // geometry
    Opt<std::string> geometryOpt = getStringOpt(expression, "geometry");

    if (!geometryOpt.ok())
    {
      return geometryOpt.error;
    }
    else if (geometryOpt.given)
    {
      subsP->subject.condition.expression.geometry = geometryOpt.value;
    }

    // coords
    Opt<std::string> coordsOpt = getStringOpt(expression, "coords");

    if (!coordsOpt.ok())
    {
        return coordsOpt.error;
    }
    else if (coordsOpt.given)
    {
      subsP->subject.condition.expression.coords = coordsOpt.value;
    }

    // georel
    Opt<std::string> georelOpt = getStringOpt(expression, "georel");
    if (!geometryOpt.ok())
    {
      return geometryOpt.error;
    }
    if (georelOpt.given)
    {
      subsP->subject.condition.expression.georel = georelOpt.value;
    }
  }
  return "";
}

/* ****************************************************************************
*
* parseAttributeList -
*
*/
static std::string parseAttributeList(ConnectionInfo* ciP, std::vector<std::string>* vec, const Value& attributes)
{
  if (!attributes.IsArray())
  {
    return error(ciP, "attrs is not an array");
  }

  for (Value::ConstValueIterator iter = attributes.Begin(); iter != attributes.End(); ++iter)
  {
    if (!iter->IsString())
    {
      return error(ciP, "attrs element is not an string");
    }

    vec->push_back(iter->GetString());
  }

  return "";
}

/* ****************************************************************************
*
* error -
*
*/
static std::string error(ConnectionInfo* ciP, const std::string& msg)
{
  alarmMgr.badInput(clientIp, msg);
  OrionError oe(SccBadRequest, msg);

  return oe.render(ciP, "");
}

<|endoftext|>


<commit_before>#include <gtest/gtest.h>

#include "../libjsapi.h"

#include <string>
#include <vector>
#include <sstream>

rs::jsapi::Runtime rt_;

class SimpleValueTests : public ::testing::Test {
protected:
    virtual void SetUp() {

    }

    virtual void TearDown() {

    }
public:
    static bool SanityCheckWhatMessage(const char* msg);
};

bool SimpleValueTests::SanityCheckWhatMessage(const char* msg) {
    while (*msg != '\0') {
        if (*msg < 0x0d || *msg > 0x7e) {
            return false;
        }
        msg++;
    }
    return true;
}

TEST_F(SimpleValueTests, test1) {
    rs::jsapi::Value value(rt_);

    ASSERT_TRUE(value.isUndefined());
    ASSERT_STREQ("undefined", value.ToString().c_str());
}

TEST_F(SimpleValueTests, test2) {
    rs::jsapi::Value value(rt_, 42);

    ASSERT_TRUE(value.isInt32());
    ASSERT_EQ(42, value.toInt32());
    ASSERT_STREQ("42", value.ToString().c_str());
}

TEST_F(SimpleValueTests, test3) {
    rs::jsapi::Value value(rt_, 3.14159);

    ASSERT_TRUE(value.isNumber());
    ASSERT_FLOAT_EQ(3.14159, value.toNumber());
    ASSERT_STREQ("3.14159", value.ToString().c_str());
}

TEST_F(SimpleValueTests, test4) {
    rs::jsapi::Value value(rt_, true);

    ASSERT_TRUE(value.isBoolean());
    ASSERT_TRUE(value.toBoolean());
    ASSERT_STREQ("true", value.ToString().c_str());
}

TEST_F(SimpleValueTests, test5) {
    rs::jsapi::Value value(rt_, false);

    ASSERT_TRUE(value.isBoolean());
    ASSERT_FALSE(value.toBoolean());
    ASSERT_STREQ("false", value.ToString().c_str());
}

TEST_F(SimpleValueTests, test6) {
    rs::jsapi::Value value(rt_, "hello world");

    ASSERT_TRUE(value.isString());
    ASSERT_STREQ("hello world", value.ToString().c_str());
}

TEST_F(SimpleValueTests, test7) {
    std::string str("hello world");
    rs::jsapi::Value value(rt_, str);

    ASSERT_TRUE(value.isString());
    ASSERT_STREQ("hello world", value.ToString().c_str());
}

TEST_F(SimpleValueTests, test7b) {
    std::string str(512, '1');
    rs::jsapi::Value value(rt_, str);

    ASSERT_TRUE(value.isString());
    ASSERT_STREQ(str.c_str(), value.ToString().c_str());
}

TEST_F(SimpleValueTests, test8) {
    rs::jsapi::Value value(rt_);

    value.set(42);
    ASSERT_TRUE(value.isInt32());
    ASSERT_EQ(42, value.toInt32());
    ASSERT_STREQ("42", value.ToString().c_str());

    value.set(3.14159);
    ASSERT_TRUE(value.isNumber());
    ASSERT_FLOAT_EQ(3.14159, value.toNumber());
    ASSERT_STREQ("3.14159", value.ToString().c_str());

    value.set(true);
    ASSERT_TRUE(value.isBoolean());
    ASSERT_TRUE(value.toBoolean());
    ASSERT_STREQ("true", value.ToString().c_str());

    value.set(false);
    ASSERT_TRUE(value.isBoolean());
    ASSERT_FALSE(value.toBoolean());
    ASSERT_STREQ("false", value.ToString().c_str());

    value.setUndefined();
    ASSERT_TRUE(value.isUndefined());
    ASSERT_STREQ("undefined", value.ToString().c_str());

    value.set("hello world");
    ASSERT_TRUE(value.isString());
    ASSERT_STREQ("hello world", value.ToString().c_str());

    value.setNull();
    ASSERT_TRUE(value.isNull());
    ASSERT_STREQ("null", value.ToString().c_str());

    std::string str("hello world");
    value.set(str);
    ASSERT_TRUE(value.isString());
    ASSERT_STREQ("hello world", value.ToString().c_str());
}

TEST_F(SimpleValueTests, test9) {
    rs::jsapi::Value value(rt_, 42);

    std::stringstream ss;
    ss << value;
    ASSERT_STREQ("42", ss.str().c_str());
}

TEST_F(SimpleValueTests, test10) {
    rs::jsapi::Value value(rt_, true);

    std::stringstream ss;
    ss << value;
    ASSERT_STREQ("true", ss.str().c_str());
}

TEST_F(SimpleValueTests, test11) {
    rs::jsapi::Value value(rt_, false);

    std::stringstream ss;
    ss << value;
    ASSERT_STREQ("false", ss.str().c_str());
}

TEST_F(SimpleValueTests, test12) {
    rs::jsapi::Value value(rt_, 3.14159);

    std::stringstream ss;
    ss << value;
    ASSERT_STREQ("3.14159", ss.str().c_str());
}

TEST_F(SimpleValueTests, test13) {
    rs::jsapi::Value value(rt_, "hello world");

    std::stringstream ss;
    ss << value;
    ASSERT_STREQ("hello world", ss.str().c_str());
}

TEST_F(SimpleValueTests, test14) {
    std::string str(512, '1');
    rs::jsapi::Value value(rt_, str);

    std::stringstream ss;
    ss << value;
    ASSERT_STREQ(str.c_str(), ss.str().c_str());
}

TEST_F(SimpleValueTests, test15) {
    rs::jsapi::Value value(rt_, "hello world");

    JS::RootedString rv(rt_, value.toString());
    auto len = JS_GetStringLength(rv);
    ASSERT_EQ(11, len);

    std::vector<char> buffer(len);
    JS_EncodeStringToBuffer(rt_, rv, &buffer[0], len);
    std::string str(&buffer[0], len);
    ASSERT_STREQ("hello world", str.c_str());
}

TEST_F(SimpleValueTests, test16) {
    JS::RootedObject obj(rt_);

    rs::jsapi::Value value(rt_, obj);
    ASSERT_TRUE(value.isObject());
}

TEST_F(SimpleValueTests, test17) {
    rs::jsapi::Value value(rt_, JS_NewObject(rt_, nullptr, JS::NullPtr(), JS::NullPtr()));
    ASSERT_TRUE(value.isObject());
    ASSERT_STREQ("[object Object]", value.ToString().c_str());

    const JS::HandleObject& objRef = value;
    rs::jsapi::Value value2(rt_, objRef);
    ASSERT_TRUE(value2.isObject());
    ASSERT_STREQ("[object Object]", value2.ToString().c_str());
}

TEST_F(SimpleValueTests, test18) {
    JS::RootedObject obj(rt_, JS_NewArrayObject(rt_, 0));

    rs::jsapi::Value value(rt_, obj);
    ASSERT_TRUE(value.isArray());
    ASSERT_STREQ("", value.ToString().c_str());
}

TEST_F(SimpleValueTests, test19) {
    rs::jsapi::Value value(rt_);

    JS::RootedObject obj(rt_);
    value.set(obj);
    ASSERT_TRUE(value.isObject());
}

TEST_F(SimpleValueTests, test20) {
    rs::jsapi::Value value(rt_, 42);

    JS::RootedObject obj(rt_);
    value.set(obj);
    ASSERT_TRUE(value.isObject());
    ASSERT_FALSE(value.isNumber());
}

TEST_F(SimpleValueTests, test21) {
    rs::jsapi::Value value(rt_, "hello world");

    JS::RootedObject obj(rt_);
    value.set(obj);
    ASSERT_TRUE(value.isObject());
    ASSERT_FALSE(value.isString());
}

TEST_F(SimpleValueTests, test22) {
    rs::jsapi::Value value(rt_, true);

    JS::RootedObject obj(rt_);
    value.set(obj);
    ASSERT_TRUE(value.isObject());
    ASSERT_FALSE(value.isBoolean());
}

TEST_F(SimpleValueTests, test23) {
    rs::jsapi::Value value(rt_, 3.14159);

    JS::RootedObject obj(rt_);
    value.set(obj);
    ASSERT_TRUE(value.isObject());
    ASSERT_FALSE(value.isNumber());
}

TEST_F(SimpleValueTests, test24) {
    rs::jsapi::Value obj(rt_, JS_NewObject(rt_, nullptr, JS::NullPtr(), JS::NullPtr()));

    ASSERT_THROW({
        obj.toNumber();
    }, rs::jsapi::ValueCastException);

    ASSERT_THROW({
        obj.toString();
    }, rs::jsapi::ValueCastException);

    ASSERT_THROW({
        obj.toBoolean();
    }, rs::jsapi::ValueCastException);

    ASSERT_THROW({
        obj.toInt32();
    }, rs::jsapi::ValueCastException);
}

TEST_F(SimpleValueTests, test25) {
    rs::jsapi::Value value(rt_, 42);

    ASSERT_THROW({
        value.toObject();
    }, rs::jsapi::ValueCastException);

    ASSERT_THROW({
        rs::jsapi::Value result(rt_);
       JS_GetProperty(rt_, value, "test", result);
    }, rs::jsapi::ValueCastException);
}

TEST_F(SimpleValueTests, test26) {
    rs::jsapi::Value obj(rt_, JS_NewObject(rt_, nullptr, JS::NullPtr(), JS::NullPtr()));

    ASSERT_TRUE(obj.isObject());
    ASSERT_TRUE(obj.toObject() != nullptr);

    JS::RootedValue rv(rt_, obj);
    ASSERT_TRUE(rv.isObject());

    ASSERT_TRUE(obj.isObject());
    ASSERT_TRUE(obj.toObject() != nullptr);
}

TEST_F(SimpleValueTests, test27) {
    rs::jsapi::Value obj(rt_, JS_NewArrayObject(rt_, 0));

    ASSERT_TRUE(obj.isArray());
    ASSERT_TRUE(obj.toObject() != nullptr);

    JS::RootedValue rv(rt_, obj);
    ASSERT_TRUE(rv.isObject());

    ASSERT_TRUE(obj.isArray());
    ASSERT_TRUE(obj.toObject() != nullptr);
}

TEST_F(SimpleValueTests, test28) {
    bool thrown = false;
    rs::jsapi::Value obj(rt_, 42);

    try {
        obj.toObject();
    } catch (const rs::jsapi::ValueCastException& ex) {
        SanityCheckWhatMessage(ex.what());
        thrown = true;
    }

    ASSERT_TRUE(thrown);
}<commit_msg>Added function and construct/assignment tests to Value<commit_after>#include <gtest/gtest.h>

#include "../libjsapi.h"

#include <string>
#include <vector>
#include <sstream>

rs::jsapi::Runtime rt_;

class SimpleValueTests : public ::testing::Test {
protected:
    virtual void SetUp() {

    }

    virtual void TearDown() {

    }
public:
    static bool SanityCheckWhatMessage(const char* msg);
};

bool SimpleValueTests::SanityCheckWhatMessage(const char* msg) {
    while (*msg != '\0') {
        if (*msg < 0x0d || *msg > 0x7e) {
            return false;
        }
        msg++;
    }
    return true;
}

TEST_F(SimpleValueTests, test1) {
    rs::jsapi::Value value(rt_);

    ASSERT_TRUE(value.isUndefined());
    ASSERT_STREQ("undefined", value.ToString().c_str());
}

TEST_F(SimpleValueTests, test2) {
    rs::jsapi::Value value(rt_, 42);

    ASSERT_TRUE(value.isInt32());
    ASSERT_EQ(42, value.toInt32());
    ASSERT_STREQ("42", value.ToString().c_str());
}

TEST_F(SimpleValueTests, test3) {
    rs::jsapi::Value value(rt_, 3.14159);

    ASSERT_TRUE(value.isNumber());
    ASSERT_FLOAT_EQ(3.14159, value.toNumber());
    ASSERT_STREQ("3.14159", value.ToString().c_str());
}

TEST_F(SimpleValueTests, test4) {
    rs::jsapi::Value value(rt_, true);

    ASSERT_TRUE(value.isBoolean());
    ASSERT_TRUE(value.toBoolean());
    ASSERT_STREQ("true", value.ToString().c_str());
}

TEST_F(SimpleValueTests, test5) {
    rs::jsapi::Value value(rt_, false);

    ASSERT_TRUE(value.isBoolean());
    ASSERT_FALSE(value.toBoolean());
    ASSERT_STREQ("false", value.ToString().c_str());
}

TEST_F(SimpleValueTests, test6) {
    rs::jsapi::Value value(rt_, "hello world");

    ASSERT_TRUE(value.isString());
    ASSERT_STREQ("hello world", value.ToString().c_str());
}

TEST_F(SimpleValueTests, test7) {
    std::string str("hello world");
    rs::jsapi::Value value(rt_, str);

    ASSERT_TRUE(value.isString());
    ASSERT_STREQ("hello world", value.ToString().c_str());
}

TEST_F(SimpleValueTests, test7b) {
    std::string str(512, '1');
    rs::jsapi::Value value(rt_, str);

    ASSERT_TRUE(value.isString());
    ASSERT_STREQ(str.c_str(), value.ToString().c_str());
}

TEST_F(SimpleValueTests, test8) {
    rs::jsapi::Value value(rt_);

    value.set(42);
    ASSERT_TRUE(value.isInt32());
    ASSERT_EQ(42, value.toInt32());
    ASSERT_STREQ("42", value.ToString().c_str());

    value.set(3.14159);
    ASSERT_TRUE(value.isNumber());
    ASSERT_FLOAT_EQ(3.14159, value.toNumber());
    ASSERT_STREQ("3.14159", value.ToString().c_str());

    value.set(true);
    ASSERT_TRUE(value.isBoolean());
    ASSERT_TRUE(value.toBoolean());
    ASSERT_STREQ("true", value.ToString().c_str());

    value.set(false);
    ASSERT_TRUE(value.isBoolean());
    ASSERT_FALSE(value.toBoolean());
    ASSERT_STREQ("false", value.ToString().c_str());

    value.setUndefined();
    ASSERT_TRUE(value.isUndefined());
    ASSERT_STREQ("undefined", value.ToString().c_str());

    value.set("hello world");
    ASSERT_TRUE(value.isString());
    ASSERT_STREQ("hello world", value.ToString().c_str());

    value.setNull();
    ASSERT_TRUE(value.isNull());
    ASSERT_STREQ("null", value.ToString().c_str());

    std::string str("hello world");
    value.set(str);
    ASSERT_TRUE(value.isString());
    ASSERT_STREQ("hello world", value.ToString().c_str());
}

TEST_F(SimpleValueTests, test9) {
    rs::jsapi::Value value(rt_, 42);

    std::stringstream ss;
    ss << value;
    ASSERT_STREQ("42", ss.str().c_str());
}

TEST_F(SimpleValueTests, test10) {
    rs::jsapi::Value value(rt_, true);

    std::stringstream ss;
    ss << value;
    ASSERT_STREQ("true", ss.str().c_str());
}

TEST_F(SimpleValueTests, test11) {
    rs::jsapi::Value value(rt_, false);

    std::stringstream ss;
    ss << value;
    ASSERT_STREQ("false", ss.str().c_str());
}

TEST_F(SimpleValueTests, test12) {
    rs::jsapi::Value value(rt_, 3.14159);

    std::stringstream ss;
    ss << value;
    ASSERT_STREQ("3.14159", ss.str().c_str());
}

TEST_F(SimpleValueTests, test13) {
    rs::jsapi::Value value(rt_, "hello world");

    std::stringstream ss;
    ss << value;
    ASSERT_STREQ("hello world", ss.str().c_str());
}

TEST_F(SimpleValueTests, test14) {
    std::string str(512, '1');
    rs::jsapi::Value value(rt_, str);

    std::stringstream ss;
    ss << value;
    ASSERT_STREQ(str.c_str(), ss.str().c_str());
}

TEST_F(SimpleValueTests, test15) {
    rs::jsapi::Value value(rt_, "hello world");

    JS::RootedString rv(rt_, value.toString());
    auto len = JS_GetStringLength(rv);
    ASSERT_EQ(11, len);

    std::vector<char> buffer(len);
    JS_EncodeStringToBuffer(rt_, rv, &buffer[0], len);
    std::string str(&buffer[0], len);
    ASSERT_STREQ("hello world", str.c_str());
}

TEST_F(SimpleValueTests, test16) {
    JS::RootedObject obj(rt_);

    rs::jsapi::Value value(rt_, obj);
    ASSERT_TRUE(value.isObject());
}

TEST_F(SimpleValueTests, test17) {
    rs::jsapi::Value value(rt_, JS_NewObject(rt_, nullptr, JS::NullPtr(), JS::NullPtr()));
    ASSERT_TRUE(value.isObject());
    ASSERT_STREQ("[object Object]", value.ToString().c_str());

    const JS::HandleObject& objRef = value;
    rs::jsapi::Value value2(rt_, objRef);
    ASSERT_TRUE(value2.isObject());
    ASSERT_STREQ("[object Object]", value2.ToString().c_str());
}

TEST_F(SimpleValueTests, test18) {
    JS::RootedObject obj(rt_, JS_NewArrayObject(rt_, 0));

    rs::jsapi::Value value(rt_, obj);
    ASSERT_TRUE(value.isArray());
    ASSERT_STREQ("", value.ToString().c_str());
}

TEST_F(SimpleValueTests, test19) {
    rs::jsapi::Value value(rt_);

    JS::RootedObject obj(rt_);
    value.set(obj);
    ASSERT_TRUE(value.isObject());
}

TEST_F(SimpleValueTests, test20) {
    rs::jsapi::Value value(rt_, 42);

    JS::RootedObject obj(rt_);
    value.set(obj);
    ASSERT_TRUE(value.isObject());
    ASSERT_FALSE(value.isNumber());
}

TEST_F(SimpleValueTests, test21) {
    rs::jsapi::Value value(rt_, "hello world");

    JS::RootedObject obj(rt_);
    value.set(obj);
    ASSERT_TRUE(value.isObject());
    ASSERT_FALSE(value.isString());
}

TEST_F(SimpleValueTests, test22) {
    rs::jsapi::Value value(rt_, true);

    JS::RootedObject obj(rt_);
    value.set(obj);
    ASSERT_TRUE(value.isObject());
    ASSERT_FALSE(value.isBoolean());
}

TEST_F(SimpleValueTests, test23) {
    rs::jsapi::Value value(rt_, 3.14159);

    JS::RootedObject obj(rt_);
    value.set(obj);
    ASSERT_TRUE(value.isObject());
    ASSERT_FALSE(value.isNumber());
}

TEST_F(SimpleValueTests, test24) {
    rs::jsapi::Value obj(rt_, JS_NewObject(rt_, nullptr, JS::NullPtr(), JS::NullPtr()));

    ASSERT_THROW({
        obj.toNumber();
    }, rs::jsapi::ValueCastException);

    ASSERT_THROW({
        obj.toString();
    }, rs::jsapi::ValueCastException);

    ASSERT_THROW({
        obj.toBoolean();
    }, rs::jsapi::ValueCastException);

    ASSERT_THROW({
        obj.toInt32();
    }, rs::jsapi::ValueCastException);
}

TEST_F(SimpleValueTests, test25) {
    rs::jsapi::Value value(rt_, 42);

    ASSERT_THROW({
        value.toObject();
    }, rs::jsapi::ValueCastException);

    ASSERT_THROW({
        rs::jsapi::Value result(rt_);
       JS_GetProperty(rt_, value, "test", result);
    }, rs::jsapi::ValueCastException);
}

TEST_F(SimpleValueTests, test26) {
    rs::jsapi::Value obj(rt_, JS_NewObject(rt_, nullptr, JS::NullPtr(), JS::NullPtr()));

    ASSERT_TRUE(obj.isObject());
    ASSERT_TRUE(obj.toObject() != nullptr);

    JS::RootedValue rv(rt_, obj);
    ASSERT_TRUE(rv.isObject());

    ASSERT_TRUE(obj.isObject());
    ASSERT_TRUE(obj.toObject() != nullptr);
}

TEST_F(SimpleValueTests, test27) {
    rs::jsapi::Value obj(rt_, JS_NewArrayObject(rt_, 0));

    ASSERT_TRUE(obj.isArray());
    ASSERT_TRUE(obj.toObject() != nullptr);

    JS::RootedValue rv(rt_, obj);
    ASSERT_TRUE(rv.isObject());

    ASSERT_TRUE(obj.isArray());
    ASSERT_TRUE(obj.toObject() != nullptr);
}

TEST_F(SimpleValueTests, test28) {
    bool thrown = false;
    rs::jsapi::Value obj(rt_, 42);

    try {
        obj.toObject();
    } catch (const rs::jsapi::ValueCastException& ex) {
        SanityCheckWhatMessage(ex.what());
        thrown = true;
    }

    ASSERT_TRUE(thrown);
}

TEST_F(SimpleValueTests, test29) {
    rs::jsapi::Value func(rt_);
    rt_.Evaluate("(function() { return function() { return 42; } })();", func);

    ASSERT_TRUE(func.isFunction());

    rs::jsapi::FunctionArguments args(rt_);
    rs::jsapi::Value result(rt_);
    func.CallFunction(args, result);

    ASSERT_TRUE(result.isInt32());
    ASSERT_EQ(42, result.toInt32());
}

TEST_F(SimpleValueTests, test30) {
    rs::jsapi::Value func(rt_);
    rt_.Evaluate("(function() { return function(n) { return n; } })();", func);

    ASSERT_TRUE(func.isFunction());

    rs::jsapi::FunctionArguments args(rt_);
    args.Append(3.14159);

    rs::jsapi::Value result(rt_);
    func.CallFunction(args, result);

    ASSERT_TRUE(result.isNumber());
    ASSERT_FLOAT_EQ(3.14159, result.toNumber());
}

TEST_F(SimpleValueTests, test31) {
    rs::jsapi::Value value1(rt_);
    ASSERT_TRUE(value1.isUndefined());

    rs::jsapi::Value obj(rt_);
    rs::jsapi::Object::Create(rt_,
        {},
        nullptr,
        nullptr,
        {},
        nullptr,
        obj);
    ASSERT_TRUE(!!obj);

    value1 = obj;
    ASSERT_TRUE(value1.isObject());

    rs::jsapi::Value value2(value1);
    ASSERT_TRUE(value2.isObject());
}

TEST_F(SimpleValueTests, test32) {
    rs::jsapi::Value value1(rt_, 42);
    ASSERT_TRUE(value1.isInt32());
    ASSERT_EQ(42, value1.toInt32());

    rs::jsapi::Value value2(value1);
    ASSERT_TRUE(value2.isInt32());
    ASSERT_EQ(42, value2.toInt32());
}<|endoftext|>


<commit_before>#include "dataFile.h"
#include "debugUtils.h"
#include <boost/filesystem.hpp>

DataFile::DataFile(void)
    : file(0)
    , fileIsOpen(false)
{
}

DataFile::DataFile(const std::string& in_filename)
    : file(0)
{
    open(in_filename);
}

DataFile::~DataFile(void)
{
    close();
}

void DataFile::writeScalar(const std::string& name, const double x)
{
    require_file_open();

    hid_t dataspace, dataset, datatype;
    dataspace = H5Screate_simple(0, NULL, NULL);
    datatype = H5Tcopy(H5T_NATIVE_DOUBLE);
    H5Tset_order(datatype, H5T_ORDER_LE);

    dataset = get_dataset(name, datatype, dataspace);
    H5Dwrite(dataset, datatype, H5S_ALL, H5S_ALL, H5P_DEFAULT, &x);
    H5Dclose(dataset);
}

double DataFile::readScalar(const std::string& name)
{
    require_file_open();

    hid_t dataspace, dataset;
    dataset = H5Dopen(file, name.c_str(), H5P_DEFAULT);
    dataspace = H5Dget_space(dataset);

    if (H5Sget_simple_extent_ndims(dataspace) !=  0) {
        throw debugException((format(
            "DataFile::readScalar: '%s' is not a scalar.") % name).str());
    }

    double value;
    H5Dread(dataset, H5T_NATIVE_DOUBLE, H5S_ALL, H5S_ALL, H5P_DEFAULT, &value);
    H5Dclose(dataset);
    return value;
}

void DataFile::writeVector(const std::string& name, const dvector& v)
{
    require_file_open();

    hsize_t dims = v.size();
    if (dims == 0) {
        logFile.write(format(
            "DataFile::writeVector: Warning: '%s' is empty.") % name);
        return;
    }

    hid_t dataspace, dataset, datatype;
    dataspace = H5Screate_simple(1, &dims, NULL);
    datatype = H5Tcopy(H5T_NATIVE_DOUBLE);
    H5Tset_order(datatype, H5T_ORDER_LE);

    dataset = get_dataset(name, datatype, dataspace);
    H5Dwrite(dataset, datatype, H5S_ALL, H5S_ALL, H5P_DEFAULT, &v[0]);
    H5Dclose(dataset);
}

dvector DataFile::readVector(const std::string& name)
{
    require_file_open();

    hid_t dataspace, dataset;
    dataset = H5Dopen(file, name.c_str(), H5P_DEFAULT);
    dataspace = H5Dget_space(dataset);

    if (H5Sget_simple_extent_ndims(dataspace) != 1) {
        throw debugException((format(
            "DataFile::readVector: '%s' is not a 1D array.") % name).str());
    }

    hsize_t N = H5Sget_simple_extent_npoints(dataspace);
    dvector values(N);

    H5Dread(dataset, H5T_NATIVE_DOUBLE, H5S_ALL, H5S_ALL, H5P_DEFAULT, &values[0]);
    H5Dclose(dataset);
    return values;
}

void DataFile::writeArray2D(const std::string& name, const dmatrix& y, bool transpose)
{
    require_file_open();

    dmatrix yt;
    if (transpose) {
        yt = y.transpose();
    } else {
        yt = y;
    }

    hsize_t dims[2];
    dims[0] = yt.cols(); // TODO: double check the order of these
    dims[1] = yt.rows();

    if (dims[0] == 0 || dims[1] == 0) {
        logFile.write(format(
            "DataFile::writeArray2D: Warning: '%s' is empty.") % name);
        return;
    }

    hid_t dataspace, dataset, datatype;
    dataspace = H5Screate_simple(2, dims, NULL);
    datatype = H5Tcopy(H5T_NATIVE_DOUBLE);
    H5Tset_order(datatype, H5T_ORDER_LE);

    dataset = get_dataset(name, datatype, dataspace);
    H5Dwrite(dataset, datatype, H5S_ALL, H5S_ALL, H5P_DEFAULT, yt.data());
    H5Dclose(dataset);
}

dmatrix DataFile::readArray2D(const std::string& name, bool transpose)
{
    require_file_open();

    hid_t dataspace, dataset;
    dataset = H5Dopen(file, name.c_str(), H5P_DEFAULT);
    dataspace = H5Dget_space(dataset);

    if (H5Sget_simple_extent_ndims(dataspace) != 2) {
        throw debugException((format(
            "DataFile::readVector: '%s' is not a 1D array.") % name).str());
    }

    hsize_t ndim = 2;
    vector<hsize_t> dimensions(2);
    H5Sget_simple_extent_dims(dataspace, &dimensions[0], &ndim);

    dmatrix y(dimensions[0], dimensions[1]);

    H5Dread(dataset, H5T_NATIVE_DOUBLE, H5S_ALL,
            H5S_ALL, H5P_DEFAULT, y.data());
    H5Dclose(dataset);

    if (transpose) {
        y = y.transpose().eval();
    }

    return y;
}

void DataFile::open(const std::string& in_filename)
{
    if (file) {
        close();
    }

    filename = in_filename;
    if (boost::filesystem::exists(filename)) {
        file = H5Fopen(filename.c_str(), H5F_ACC_RDWR, H5P_DEFAULT);
    } else {
        file = H5Fcreate(filename.c_str(), H5F_ACC_TRUNC,
                         H5P_DEFAULT, H5P_DEFAULT);
    }
    fileIsOpen = true;
}

void DataFile::close()
{
    if (file) {
        H5Fclose(file);
        file = 0;
    }
    fileIsOpen = false;
}

void DataFile::require_file_open()
{
    if (!file) {
        throw debugException(
            "Can't access DataFile object: H5File object not initialized.");
    } else if (!fileIsOpen) {
        throw debugException(
            "Can't access DataFile object: H5File is closed.");
    }
}

hid_t DataFile::get_dataset
(const std::string& name, hid_t datatype, hid_t dataspace)
{
    if (H5Lexists(file, name.c_str(), H5P_DEFAULT)) {
        H5Ldelete(file, name.c_str(), H5P_DEFAULT);
    }
    return H5Dcreate(file, name.c_str(), datatype, dataspace,
                     H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
}

void dataFileTest()
{
    // Test reading from / writing to an HDF5 data file
    DataFile f("test.h5");
    double x = 9001;
    f.writeScalar("x", x);

    dvector v(10);
    for (size_t i=0; i<v.size(); i++) {
        v[i] = i*i;
    }

    f.writeVector("v", v);

    dmatrix A(3,7);
    for (dmatrix::Index i=0; i<A.rows(); i++) {
        for (dmatrix::Index j=0; j<A.cols(); j++) {
            A(i,j) = 10*i + j;
        }
    }

    logFile.write(format("rows: %i") % A.rows());
    logFile.write(format("cols: %i") % A.cols());

    f.writeArray2D("A", A);
    f.close();

    DataFile g("test.h5");
    dvector w = g.readVector("v");
    for (size_t i=0; i<w.size(); i++) {
        logFile.write(format("%g") % w[i]);
    }

    logFile.write(format("%g") % g.readScalar("x"));
    logFile.write(g.readArray2D("A"));

    g.close();
}
<commit_msg>Fixed some problems reading 2D Eigen arrays from HDF5 files<commit_after>#include "dataFile.h"
#include "debugUtils.h"
#include <boost/filesystem.hpp>

DataFile::DataFile(void)
    : file(0)
    , fileIsOpen(false)
{
}

DataFile::DataFile(const std::string& in_filename)
    : file(0)
{
    open(in_filename);
}

DataFile::~DataFile(void)
{
    close();
}

void DataFile::writeScalar(const std::string& name, const double x)
{
    require_file_open();

    hid_t dataspace, dataset, datatype;
    dataspace = H5Screate_simple(0, NULL, NULL);
    datatype = H5Tcopy(H5T_NATIVE_DOUBLE);
    H5Tset_order(datatype, H5T_ORDER_LE);

    dataset = get_dataset(name, datatype, dataspace);
    H5Dwrite(dataset, datatype, H5S_ALL, H5S_ALL, H5P_DEFAULT, &x);
    H5Dclose(dataset);
}

double DataFile::readScalar(const std::string& name)
{
    require_file_open();

    hid_t dataspace, dataset;
    dataset = H5Dopen(file, name.c_str(), H5P_DEFAULT);
    dataspace = H5Dget_space(dataset);

    if (H5Sget_simple_extent_ndims(dataspace) !=  0) {
        throw debugException((format(
            "DataFile::readScalar: '%s' is not a scalar.") % name).str());
    }

    double value;
    H5Dread(dataset, H5T_NATIVE_DOUBLE, H5S_ALL, H5S_ALL, H5P_DEFAULT, &value);
    H5Dclose(dataset);
    return value;
}

void DataFile::writeVector(const std::string& name, const dvector& v)
{
    require_file_open();

    hsize_t dims = v.size();
    if (dims == 0) {
        logFile.write(format(
            "DataFile::writeVector: Warning: '%s' is empty.") % name);
        return;
    }

    hid_t dataspace, dataset, datatype;
    dataspace = H5Screate_simple(1, &dims, NULL);
    datatype = H5Tcopy(H5T_NATIVE_DOUBLE);
    H5Tset_order(datatype, H5T_ORDER_LE);

    dataset = get_dataset(name, datatype, dataspace);
    H5Dwrite(dataset, datatype, H5S_ALL, H5S_ALL, H5P_DEFAULT, &v[0]);
    H5Dclose(dataset);
}

dvector DataFile::readVector(const std::string& name)
{
    require_file_open();

    hid_t dataspace, dataset;
    dataset = H5Dopen(file, name.c_str(), H5P_DEFAULT);
    dataspace = H5Dget_space(dataset);

    if (H5Sget_simple_extent_ndims(dataspace) != 1) {
        throw debugException((format(
            "DataFile::readVector: '%s' is not a 1D array.") % name).str());
    }

    hsize_t N = H5Sget_simple_extent_npoints(dataspace);
    dvector values(N);

    H5Dread(dataset, H5T_NATIVE_DOUBLE, H5S_ALL, H5S_ALL, H5P_DEFAULT, &values[0]);
    H5Dclose(dataset);
    return values;
}

void DataFile::writeArray2D(const std::string& name, const dmatrix& y, bool transpose)
{
    require_file_open();

    dmatrix yt;
    if (transpose) {
        yt = y.transpose();
    } else {
        yt = y;
    }

    hsize_t dims[2];
    dims[0] = yt.cols(); // TODO: double check the order of these
    dims[1] = yt.rows();

    if (dims[0] == 0 || dims[1] == 0) {
        logFile.write(format(
            "DataFile::writeArray2D: Warning: '%s' is empty.") % name);
        return;
    }

    hid_t dataspace, dataset, datatype;
    dataspace = H5Screate_simple(2, dims, NULL);
    datatype = H5Tcopy(H5T_NATIVE_DOUBLE);
    H5Tset_order(datatype, H5T_ORDER_LE);

    dataset = get_dataset(name, datatype, dataspace);
    H5Dwrite(dataset, datatype, H5S_ALL, H5S_ALL, H5P_DEFAULT, yt.data());
    H5Dclose(dataset);
}

dmatrix DataFile::readArray2D(const std::string& name, bool transpose)
{
    require_file_open();

    hid_t dataspace, dataset;
    dataset = H5Dopen(file, name.c_str(), H5P_DEFAULT);
    dataspace = H5Dget_space(dataset);

    if (H5Sget_simple_extent_ndims(dataspace) != 2) {
        throw debugException((format(
            "DataFile::readVector: '%s' is not a 2D array.\n"
            "(file: '%s'; ndims: %i") % name % filename %
            H5Sget_simple_extent_ndims(dataspace)).str());
    }

    hsize_t ndim = 2;
    vector<hsize_t> dimensions(2);
    H5Sget_simple_extent_dims(dataspace, &dimensions[0], &ndim);

    dmatrix y(dimensions[1], dimensions[0]);

    H5Dread(dataset, H5T_NATIVE_DOUBLE, H5S_ALL,
            H5S_ALL, H5P_DEFAULT, y.data());
    H5Dclose(dataset);

    if (transpose) {
        y = y.transpose().eval();
    }

    return y;
}

void DataFile::open(const std::string& in_filename)
{
    if (file) {
        close();
    }

    filename = in_filename;
    if (boost::filesystem::exists(filename)) {
        file = H5Fopen(filename.c_str(), H5F_ACC_RDWR, H5P_DEFAULT);
    } else {
        file = H5Fcreate(filename.c_str(), H5F_ACC_TRUNC,
                         H5P_DEFAULT, H5P_DEFAULT);
    }
    fileIsOpen = true;
}

void DataFile::close()
{
    if (file) {
        H5Fclose(file);
        file = 0;
    }
    fileIsOpen = false;
}

void DataFile::require_file_open()
{
    if (!file) {
        throw debugException(
            "Can't access DataFile object: H5File object not initialized.");
    } else if (!fileIsOpen) {
        throw debugException(
            "Can't access DataFile object: H5File is closed.");
    }
}

hid_t DataFile::get_dataset
(const std::string& name, hid_t datatype, hid_t dataspace)
{
    if (H5Lexists(file, name.c_str(), H5P_DEFAULT)) {
        H5Ldelete(file, name.c_str(), H5P_DEFAULT);
    }
    return H5Dcreate(file, name.c_str(), datatype, dataspace,
                     H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
}

void dataFileTest()
{
    // Test reading from / writing to an HDF5 data file
    DataFile f("test.h5");
    double x = 9001;
    f.writeScalar("x", x);

    dvector v(10);
    for (size_t i=0; i<v.size(); i++) {
        v[i] = i*i;
    }

    f.writeVector("v", v);

    dmatrix A(3,7);
    for (dmatrix::Index i=0; i<A.rows(); i++) {
        for (dmatrix::Index j=0; j<A.cols(); j++) {
            A(i,j) = 10*i + j;
        }
    }

    logFile.write(format("rows: %i") % A.rows());
    logFile.write(format("cols: %i") % A.cols());

    f.writeArray2D("A", A);
    f.close();

    DataFile g("test.h5");
    dvector w = g.readVector("v");
    for (size_t i=0; i<w.size(); i++) {
        logFile.write(format("%g") % w[i]);
    }

    logFile.write(format("%g") % g.readScalar("x"));
    logFile.write(g.readArray2D("A"));

    g.close();
}
<|endoftext|>


<commit_before>#include <iostream>
#include <algorithm>

#include <unistd.h>
#include <fcntl.h>

#include "dasync.h"

#include "service.h"
#include "dinit-log.h"
#include "cpbuffer.h"

extern EventLoop_t eventLoop;

LogLevel log_level = LogLevel::WARN;
LogLevel cons_log_level = LogLevel::WARN;

static bool log_to_console = false;   // whether we should output log messages to
                                     // console immediately
static bool log_current_line;  // Whether the current line is being logged

static ServiceSet *service_set = nullptr;  // Reference to service set

namespace {
class BufferedLogStream : public PosixFdWatcher<NullMutex>
{
    private:

    // Outgoing:
    bool partway = false;     // if we are partway throught output of a log message
    bool discarded = false;   // if we have discarded a message

    // A "special message" is not stored in the circular buffer; instead
    // it is delivered from an external buffer not managed by BufferedLogger.
    bool special = false;      // currently outputting special message?
    char *special_buf; // buffer containing special message
    int msg_index;     // index into special message

    public:
    CPBuffer<4096> log_buffer;

    // Incoming:
    int current_index = 0;    // current/next incoming message index

    int fd;

    void init(int fd)
    {
        this->fd = fd;
    }

    Rearm gotEvent(EventLoop_t *loop, int fd, int flags) noexcept override;

    // Check whether the console can be released.
    void flushForRelease();
};
}

// Two log streams:
// (One for main log, one for console)
static BufferedLogStream log_stream[2];

constexpr static int DLOG_MAIN = 0; // main log facility
constexpr static int DLOG_CONS = 1; // console

static void release_console()
{
    if (! log_to_console) {
        int flags = fcntl(1, F_GETFL, 0);
        fcntl(1, F_SETFL, flags & ~O_NONBLOCK);
        service_set->pullConsoleQueue();
    }
}

void BufferedLogStream::flushForRelease()
{
    // Try to flush any messages that are currently buffered. (Console is non-blocking
    // so it will fail gracefully).
    if (gotEvent(&eventLoop, fd, out_events) == Rearm::DISARM) {
        // Console has already been released at this point.
        setEnabled(&eventLoop, false);
    }
    // gotEvent didn't want to disarm, so must be partway through a message; will
    // release when it's finished.
}

Rearm BufferedLogStream::gotEvent(EventLoop_t *loop, int fd, int flags) noexcept
{
    // TODO correct for the case that this is *not* the console log stream.

    auto &log_stream = *this;

    if ((! partway) && log_stream.special) {
        char * start = log_stream.special_buf + log_stream.msg_index;
        char * end = std::find(log_stream.special_buf + log_stream.msg_index, (char *)nullptr, '\n');
        int r = write(fd, start, end - start + 1);
        if (r >= 0) {
            if (start + r > end) {
                // All written: go on to next message in queue
                log_stream.special = false;
                log_stream.partway = false;
                log_stream.msg_index = 0;

                if (!log_to_console) {
                    release_console();
                    return Rearm::DISARM;
                }
            }
            else {
                log_stream.msg_index += r;
                return Rearm::REARM;
            }
        }
        else {
            // spurious readiness - EAGAIN or EWOULDBLOCK?
            // other error?
            // TODO
        }
        return Rearm::REARM;
    }
    else {
        // Writing from the regular circular buffer

        // TODO issue special message if we have discarded a log message

        if (log_stream.current_index == 0) {
            release_console();
            return Rearm::DISARM;
        }

        char *ptr = log_stream.log_buffer.get_ptr(0);
        int len = log_stream.log_buffer.get_contiguous_length(ptr);
        char *creptr = ptr + len;  // contiguous region end
        char *eptr = std::find(ptr, creptr, '\n');

        bool will_complete = false;  // will complete this message?
        if (eptr != creptr) {
            eptr++;  // include '\n'
            will_complete = true;
        }

        len = eptr - ptr;

        int r = write(fd, ptr, len);

        if (r >= 0) {
            bool complete = (r == len) && will_complete;
            log_stream.log_buffer.consume(len);
            log_stream.partway = ! complete;
            if (complete) {
                log_stream.current_index -= len;
                if (log_stream.current_index == 0 || !log_to_console) {
                    // No more messages buffered / stop logging to console:
                    release_console();
                    return Rearm::DISARM;
                }
            }
        }
        else {
            // TODO
            // EAGAIN / EWOULDBLOCK?
            // error?
            return Rearm::REARM;
        }
    }

    // We've written something by the time we get here. We could fall through to below, but
    // let's give other events a chance to be processed by returning now.
    return Rearm::REARM;
}

void init_log(ServiceSet *sset) noexcept
{
    service_set = sset;
    log_stream[DLOG_CONS].registerWith(&eventLoop, STDOUT_FILENO, out_events); // TODO register in disabled state
    enable_console_log(true);
}

bool is_log_flushed() noexcept
{
    return log_stream[DLOG_CONS].current_index == 0;
}

// Enable or disable console logging. If disabled, console logging will be disabled on the
// completion of output of the current message (if any), at which point the first service record
// queued in the service set will acquire the console.
void enable_console_log(bool enable) noexcept
{
    if (enable && ! log_to_console) {
        // Console is fd 1 - stdout
        // Set non-blocking IO:
        int flags = fcntl(1, F_GETFL, 0);
        fcntl(1, F_SETFL, flags | O_NONBLOCK);
        // Activate watcher:
        log_stream[DLOG_CONS].init(STDOUT_FILENO);
        log_to_console = true;
    }
    else if (! enable && log_to_console) {
        log_to_console = false;
        log_stream[DLOG_CONS].flushForRelease();
    }
}

// Variadic method to calculate the sum of string lengths:
static int sum_length(const char *arg) noexcept
{
    return std::strlen(arg);
}

template <typename U, typename ... T> static int sum_length(U first, T ... args) noexcept
{
    return sum_length(first) + sum_length(args...);
}

// Variadic method to append strings to a buffer:
static void append(CPBuffer<4096> &buf, const char *s)
{
    buf.append(s, std::strlen(s));
}

template <typename U, typename ... T> static void append(CPBuffer<4096> &buf, U u, T ... t)
{
    append(buf, u);
    append(buf, t...);
}

// Variadic method to log a sequence of strings as a single message:
template <typename ... T> static void do_log(T ... args) noexcept
{
    int amount = sum_length(args...);
    if (log_stream[DLOG_CONS].log_buffer.get_free() >= amount) {
        append(log_stream[DLOG_CONS].log_buffer, args...);

        bool was_first = (log_stream[DLOG_CONS].current_index == 0);
        log_stream[DLOG_CONS].current_index += amount;
        if (was_first && log_to_console) {
            log_stream[DLOG_CONS].setEnabled(&eventLoop, true);
        }
    }
    else {
        // TODO mark a discarded message
    }
}

// Variadic method to potentially log a sequence of strings as a single message with the given log level:
template <typename ... T> static void do_log(LogLevel lvl, T ... args) noexcept
{
    if (lvl >= cons_log_level) {
        do_log(args...);
    }
}

// Log a message. A newline will be appended.
void log(LogLevel lvl, const char *msg) noexcept
{
    do_log(lvl, "dinit: ", msg, "\n");
}

// Log part of a message. A series of calls to do_log_part must be followed by a call to do_log_commit.
template <typename T> static void do_log_part(T arg) noexcept
{
    int amount = sum_length(arg);
    if (log_stream[DLOG_CONS].log_buffer.get_free() >= amount) {
        append(log_stream[DLOG_CONS].log_buffer, arg);
    }
    else {
        // reset
        log_stream[DLOG_CONS].log_buffer.trim_to(log_stream[DLOG_CONS].current_index);
        log_current_line = false;
        // TODO mark discarded message
    }
}

// Commit a message that was issued as a series of parts (via do_log_part).
static void do_log_commit() noexcept
{
    if (log_current_line) {
        bool was_first = log_stream[DLOG_CONS].current_index == 0;
        log_stream[DLOG_CONS].current_index = log_stream[DLOG_CONS].log_buffer.get_length();
        if (was_first && log_to_console) {
            log_stream[DLOG_CONS].setEnabled(&eventLoop, true);
        }
    }
}

// Log a multi-part message beginning
void logMsgBegin(LogLevel lvl, const char *msg) noexcept
{
    // TODO use buffer
    log_current_line = lvl >= log_level;
    if (log_current_line) {
        if (log_to_console) {
            do_log_part("dinit: ");
            do_log_part(msg);
        }
    }
}

// Continue a multi-part log message
void logMsgPart(const char *msg) noexcept
{
    // TODO use buffer
    if (log_current_line) {
        if (log_to_console) {
            do_log_part(msg);
        }
    }
}

// Complete a multi-part log message
void logMsgEnd(const char *msg) noexcept
{
    // TODO use buffer
    if (log_current_line) {
        if (log_to_console) {
            do_log_part(msg);
            do_log_part("\n");
            do_log_commit();
        }
    }
}

void logServiceStarted(const char *service_name) noexcept
{
    do_log("[  OK  ] ", service_name, "\n");
}

void logServiceFailed(const char *service_name) noexcept
{
    do_log("[FAILED] ", service_name, "\n");
}

void logServiceStopped(const char *service_name) noexcept
{
    do_log("[STOPPD] ", service_name, "\n");
}
<commit_msg>Refactoring, and actually enable console log in enable_console_log()...<commit_after>#include <iostream>
#include <algorithm>

#include <unistd.h>
#include <fcntl.h>

#include "dasync.h"

#include "service.h"
#include "dinit-log.h"
#include "cpbuffer.h"

extern EventLoop_t eventLoop;

LogLevel log_level = LogLevel::WARN;
LogLevel cons_log_level = LogLevel::WARN;

static bool log_to_console = false;   // whether we should output log messages to
                                     // console immediately
static bool log_current_line;  // Whether the current line is being logged

static ServiceSet *service_set = nullptr;  // Reference to service set

namespace {
class BufferedLogStream : public PosixFdWatcher<NullMutex>
{
    private:

    // Outgoing:
    bool partway = false;     // if we are partway throught output of a log message
    bool discarded = false;   // if we have discarded a message

    // A "special message" is not stored in the circular buffer; instead
    // it is delivered from an external buffer not managed by BufferedLogger.
    bool special = false;      // currently outputting special message?
    char *special_buf; // buffer containing special message
    int msg_index;     // index into special message

    CPBuffer<4096> log_buffer;

    public:

    // Incoming:
    int current_index = 0;    // current/next incoming message index

    int fd;

    void init(int fd)
    {
        this->fd = fd;
    }

    Rearm gotEvent(EventLoop_t *loop, int fd, int flags) noexcept override;

    // Check whether the console can be released.
    void flushForRelease();

    // Commit a log message
    void commit_msg()
    {
        bool was_first = current_index == 0;
        current_index = log_buffer.get_length();
        if (was_first && log_to_console) {
            setEnabled(&eventLoop, true);
        }
    }

    void rollback_msg()
    {
        log_buffer.trim_to(current_index);
    }

    int get_free()
    {
        return log_buffer.get_free();
    }

    void append(const char *s, size_t len)
    {
        log_buffer.append(s, len);
    }
};
}

// Two log streams:
// (One for main log, one for console)
static BufferedLogStream log_stream[2];

constexpr static int DLOG_MAIN = 0; // main log facility
constexpr static int DLOG_CONS = 1; // console

static void release_console()
{
    if (! log_to_console) {
        int flags = fcntl(1, F_GETFL, 0);
        fcntl(1, F_SETFL, flags & ~O_NONBLOCK);
        service_set->pullConsoleQueue();
    }
}

void BufferedLogStream::flushForRelease()
{
    // Try to flush any messages that are currently buffered. (Console is non-blocking
    // so it will fail gracefully).
    if (gotEvent(&eventLoop, fd, out_events) == Rearm::DISARM) {
        // Console has already been released at this point.
        setEnabled(&eventLoop, false);
    }
    // gotEvent didn't want to disarm, so must be partway through a message; will
    // release when it's finished.
}

Rearm BufferedLogStream::gotEvent(EventLoop_t *loop, int fd, int flags) noexcept
{
    // TODO correct for the case that this is *not* the console log stream.

    auto &log_stream = *this;

    if ((! partway) && log_stream.special) {
        char * start = log_stream.special_buf + log_stream.msg_index;
        char * end = std::find(log_stream.special_buf + log_stream.msg_index, (char *)nullptr, '\n');
        int r = write(fd, start, end - start + 1);
        if (r >= 0) {
            if (start + r > end) {
                // All written: go on to next message in queue
                log_stream.special = false;
                log_stream.partway = false;
                log_stream.msg_index = 0;

                if (!log_to_console) {
                    release_console();
                    return Rearm::DISARM;
                }
            }
            else {
                log_stream.msg_index += r;
                return Rearm::REARM;
            }
        }
        else {
            // spurious readiness - EAGAIN or EWOULDBLOCK?
            // other error?
            // TODO
        }
        return Rearm::REARM;
    }
    else {
        // Writing from the regular circular buffer

        // TODO issue special message if we have discarded a log message

        if (log_stream.current_index == 0) {
            release_console();
            return Rearm::DISARM;
        }

        char *ptr = log_stream.log_buffer.get_ptr(0);
        int len = log_stream.log_buffer.get_contiguous_length(ptr);
        char *creptr = ptr + len;  // contiguous region end
        char *eptr = std::find(ptr, creptr, '\n');

        bool will_complete = false;  // will complete this message?
        if (eptr != creptr) {
            eptr++;  // include '\n'
            will_complete = true;
        }

        len = eptr - ptr;

        int r = write(fd, ptr, len);

        if (r >= 0) {
            bool complete = (r == len) && will_complete;
            log_stream.log_buffer.consume(len);
            log_stream.partway = ! complete;
            if (complete) {
                log_stream.current_index -= len;
                if (log_stream.current_index == 0 || !log_to_console) {
                    // No more messages buffered / stop logging to console:
                    release_console();
                    return Rearm::DISARM;
                }
            }
        }
        else {
            // TODO
            // EAGAIN / EWOULDBLOCK?
            // error?
            return Rearm::REARM;
        }
    }

    // We've written something by the time we get here. We could fall through to below, but
    // let's give other events a chance to be processed by returning now.
    return Rearm::REARM;
}

void init_log(ServiceSet *sset) noexcept
{
    service_set = sset;
    log_stream[DLOG_CONS].registerWith(&eventLoop, STDOUT_FILENO, out_events); // TODO register in disabled state
    enable_console_log(true);
}

bool is_log_flushed() noexcept
{
    return log_stream[DLOG_CONS].current_index == 0;
}

// Enable or disable console logging. If disabled, console logging will be disabled on the
// completion of output of the current message (if any), at which point the first service record
// queued in the service set will acquire the console.
void enable_console_log(bool enable) noexcept
{
    if (enable && ! log_to_console) {
        // Console is fd 1 - stdout
        // Set non-blocking IO:
        int flags = fcntl(1, F_GETFL, 0);
        fcntl(1, F_SETFL, flags | O_NONBLOCK);
        // Activate watcher:
        log_stream[DLOG_CONS].init(STDOUT_FILENO);
        log_stream[DLOG_CONS].setEnabled(&eventLoop, true);
        log_to_console = true;
    }
    else if (! enable && log_to_console) {
        log_to_console = false;
        log_stream[DLOG_CONS].flushForRelease();
    }
}

// Variadic method to calculate the sum of string lengths:
static int sum_length(const char *arg) noexcept
{
    return std::strlen(arg);
}

template <typename U, typename ... T> static int sum_length(U first, T ... args) noexcept
{
    return sum_length(first) + sum_length(args...);
}

// Variadic method to append strings to a buffer:
static void append(BufferedLogStream &buf, const char *s)
{
    buf.append(s, std::strlen(s));
}

template <typename U, typename ... T> static void append(BufferedLogStream &buf, U u, T ... t)
{
    append(buf, u);
    append(buf, t...);
}

// Variadic method to log a sequence of strings as a single message:
template <typename ... T> static void do_log(T ... args) noexcept
{
    int amount = sum_length(args...);
    if (log_stream[DLOG_CONS].get_free() >= amount) {
        append(log_stream[DLOG_CONS], args...);

        bool was_first = (log_stream[DLOG_CONS].current_index == 0);
        log_stream[DLOG_CONS].current_index += amount;
        if (was_first && log_to_console) {
            log_stream[DLOG_CONS].setEnabled(&eventLoop, true);
        }
    }
    else {
        // TODO mark a discarded message
    }
}

// Variadic method to potentially log a sequence of strings as a single message with the given log level:
template <typename ... T> static void do_log(LogLevel lvl, T ... args) noexcept
{
    if (lvl >= cons_log_level) {
        do_log(args...);
    }
}

// Log a message. A newline will be appended.
void log(LogLevel lvl, const char *msg) noexcept
{
    do_log(lvl, "dinit: ", msg, "\n");
}

// Log part of a message. A series of calls to do_log_part must be followed by a call to do_log_commit.
template <typename T> static void do_log_part(T arg) noexcept
{
    int amount = sum_length(arg);
    if (log_stream[DLOG_CONS].get_free() >= amount) {
        append(log_stream[DLOG_CONS], arg);
    }
    else {
        log_stream[DLOG_CONS].rollback_msg();
        log_current_line = false;
        // TODO mark discarded message
    }
}

// Commit a message that was issued as a series of parts (via do_log_part).
static void do_log_commit() noexcept
{
    if (log_current_line) {
        log_stream[DLOG_CONS].commit_msg();
    }
}

// Log a multi-part message beginning
void logMsgBegin(LogLevel lvl, const char *msg) noexcept
{
    // TODO use buffer
    log_current_line = lvl >= log_level;
    if (log_current_line) {
        if (log_to_console) {
            do_log_part("dinit: ");
            do_log_part(msg);
        }
    }
}

// Continue a multi-part log message
void logMsgPart(const char *msg) noexcept
{
    // TODO use buffer
    if (log_current_line) {
        if (log_to_console) {
            do_log_part(msg);
        }
    }
}

// Complete a multi-part log message
void logMsgEnd(const char *msg) noexcept
{
    // TODO use buffer
    if (log_current_line) {
        if (log_to_console) {
            do_log_part(msg);
            do_log_part("\n");
            do_log_commit();
        }
    }
}

void logServiceStarted(const char *service_name) noexcept
{
    do_log("[  OK  ] ", service_name, "\n");
}

void logServiceFailed(const char *service_name) noexcept
{
    do_log("[FAILED] ", service_name, "\n");
}

void logServiceStopped(const char *service_name) noexcept
{
    do_log("[STOPPD] ", service_name, "\n");
}
<|endoftext|>


<commit_before>//
// This file is part of MipTknzr Library Project
// Copyright (c) Antonino Calderone (antonino.calderone@gmail.com)
// All rights reserved.
// Licensed under the MIT License.
// See COPYING file in the project root for full license information.
//

/* -------------------------------------------------------------------------- */

#include <assert.h>
#include <sstream>
#include <iostream>
#include <list>
#include <vector>
#include <unordered_map>
#include <memory>
#include <set>
#include <map>
#include <fstream>
#include <string>

#ifdef _UNICODE
#include <locale>
#include <codecvt>
#include <iomanip>
#endif

#include "mip_json_obj.h"
#include "mip_json_parser.h"

/* -------------------------------------------------------------------------- */

int main(int argc, mip::char_t* argv[])
{
    if (argc<2) {
        std::_cerr << _T("File name missing") << std::endl;
        return 1;
    }

    mip::json_parser_t parser(& std::_cout);

    const mip::char_t* filename = argv[1];

    mip::_ifstream is(filename, std::ios::in | std::ios::binary);

    if (!is.is_open()) {
        std::_cerr << _T("Cannot open ") << filename << std::endl;
        return false;
    }

#ifdef _UNICODE
    const std::locale utf16_locale
        = std::locale(
            std::locale(),
            new std::codecvt_utf16<
            wchar_t,
            0x10ffff,
            std::codecvt_mode(std::little_endian | std::consume_header)>());

    is.imbue(utf16_locale);
#endif

    if (!is.is_open() || is.bad()) {
        std::cerr << _T("error reading the input stream");
        return 1;
    }

    std::_cout << _T("Parsing ...") << std::endl << std::endl;

    mip::_stringstream errlog;
    auto res = parser.parse(is, errlog);

    std::_cout
        << std::endl << std::endl
        << _T("...done")
        << std::endl << std::endl;

    if (! res.first) {
        std::_cerr
            << std::endl
            << _T("JSON text is Invalid: ")
            << errlog.str()
            << std::endl;
        return 1;
    }

    std::_cout
        << std::endl
        << std::endl
        << _T("JSON is Valid")
        << std::endl
        << std::endl;

    std::_cout
        << _T("Compact representation")
        << std::endl
        << std::endl;

    if (res.second) {
        std::_cout
            << *res.second
            << std::endl;
    }
    else {
        std::_cout << _T("null") << std::endl;
    }

    return 0;
}
<commit_msg>Update jsonvalid.cc<commit_after>//
// This file is part of MipTknzr Library Project
// Copyright (c) Antonino Calderone (antonino.calderone@gmail.com)
// All rights reserved.
// Licensed under the MIT License.
// See COPYING file in the project root for full license information.
//

/* -------------------------------------------------------------------------- */

#include <assert.h>
#include <sstream>
#include <iostream>
#include <list>
#include <vector>
#include <unordered_map>
#include <memory>
#include <set>
#include <map>
#include <fstream>
#include <string>

#ifdef _UNICODE
#include <locale>
#include <codecvt>
#include <iomanip>
#endif

#include "mip_json_obj.h"
#include "mip_json_parser.h"

/* -------------------------------------------------------------------------- */

int main(int argc, mip::char_t* argv[])
{
    if (argc<2) {
        std::_cerr << _T("File name missing") << std::endl;
        return 1;
    }

    mip::json_parser_t parser(& std::_cout);

    const mip::char_t* filename = argv[1];

    mip::_ifstream is(filename, std::ios::in | std::ios::binary);

    if (!is.is_open()) {
        std::_cerr << _T("Cannot open ") << filename << std::endl;
        return 1;
    }

#ifdef _UNICODE
    const std::locale utf16_locale
        = std::locale(
            std::locale(),
            new std::codecvt_utf16<
            wchar_t,
            0x10ffff,
            std::codecvt_mode(std::little_endian | std::consume_header)>());

    is.imbue(utf16_locale);
#endif

    if (!is.is_open() || is.bad()) {
        std::cerr << _T("error reading the input stream");
        return 1;
    }

    std::_cout << _T("Parsing ...") << std::endl << std::endl;

    mip::_stringstream errlog;
    auto res = parser.parse(is, errlog);

    std::_cout
        << std::endl << std::endl
        << _T("...done")
        << std::endl << std::endl;

    if (! res.first) {
        std::_cerr
            << std::endl
            << _T("JSON text is Invalid: ")
            << errlog.str()
            << std::endl;
        return 1;
    }

    std::_cout
        << std::endl
        << std::endl
        << _T("JSON is Valid")
        << std::endl
        << std::endl;

    std::_cout
        << _T("Compact representation")
        << std::endl
        << std::endl;

    if (res.second) {
        std::_cout
            << *res.second
            << std::endl;
    }
    else {
        std::_cout << _T("null") << std::endl;
    }

    return 0;
}
<|endoftext|>


<commit_before>#include "FileOps.h"

#include "Platform.h"
#include "StringUtils.h"

#include <malloc.h>
#include <string.h>
#include <fstream>

#include "minizip/unzip.h"

#ifdef PLATFORM_UNIX
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <libgen.h>
#endif

FileOps::IOException::IOException(const std::string& error)
: m_errno(0)
{
	m_error = error;

#ifdef PLATFORM_UNIX
	m_errno = errno;

	if (m_errno > 0)
	{
		m_error += " details: " + std::string(strerror(m_errno));
	}
#endif
}

FileOps::IOException::~IOException() throw ()
{
}

bool FileOps::fileExists(const char* path) throw (IOException)
{
#ifdef PLATFORM_UNIX
	struct stat fileInfo;
	if (stat(path,&fileInfo) != 0)
	{
		if (errno == ENOENT)
		{
			return false;
		}
		else
		{
			throw IOException("Error checking for file " + std::string(path));
		}
	}
	return true;
#else
	throw IOException("not implemented");
#endif
}

void FileOps::setPermissions(const char* path, int permissions) throw (IOException)
{
#ifdef PLATFORM_UNIX
	// TODO - Convert permissions correctly
	int mode = permissions;
	if (chmod(path,mode) != 0)
	{
		throw IOException("Failed to set permissions on " + std::string(path) + " to " + intToStr(permissions));
	}
#else
	throw IOException("not implemented");
#endif
}

void FileOps::moveFile(const char* src, const char* dest) throw (IOException)
{
#ifdef PLATFORM_UNIX
	if (rename(src,dest) != 0)
	{
		throw IOException("Unable to rename " + std::string(src) + " to " + std::string(dest));
	}
#else
	throw IOException("not implemented");
#endif
}

void FileOps::extractFromZip(const char* zipFilePath, const char* src, const char* dest) throw (IOException)
{
	unzFile zipFile = unzOpen(zipFilePath);
	int result = unzLocateFile(zipFile,src,0);
	if (result == UNZ_OK)
	{
		// found a match which is now the current file
		unzOpenCurrentFile(zipFile);
		int chunkSize = 4096;
		char buffer[chunkSize];

		std::ofstream outputFile(dest,std::ofstream::binary);
		if (!outputFile.good())
		{
			throw IOException("Unable to write to file " + std::string(src));
		}
		while (true)
		{
			int count = unzReadCurrentFile(zipFile,buffer,chunkSize);
			if (count <= 0)
			{
				if (count < 0)
				{
					throw IOException("Error extracting file from archive " + std::string(src));
				}
				break;
			}
			outputFile.write(buffer,count);
		}
		outputFile.close();

		unzCloseCurrentFile(zipFile);
	}
	else
	{
		throw IOException("Unable to find file " + std::string(src) + " in zip archive " + std::string(zipFilePath));
	}
	unzClose(zipFile);
}

void FileOps::mkdir(const char* dir) throw (IOException)
{
#ifdef PLATFORM_UNIX
	if (::mkdir(dir,S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) != 0)
	{
		throw IOException("Unable to create directory " + std::string(dir));
	}
#else
	throw IOException("not implemented");
#endif
}

void FileOps::rmdir(const char* dir) throw (IOException)
{
#ifdef PLATFORM_UNIX
	if (::rmdir(dir) != 0)
	{
		throw IOException("Unable to remove directory " + std::string(dir));
	}
#else
	throw IOException("not implemented");
#endif
}

void FileOps::createSymLink(const char* link, const char* target) throw (IOException)
{
#ifdef PLATFORM_UNIX
	if (symlink(target,link) != 0)
	{
		throw IOException("Unable to create symlink " + std::string(link) + " to " + std::string(target));
	}
#else
	// symlinks are not supported under Windows (at least, not universally.
	// Windows Vista and later do actually support symlinks)
	throw IOException("not implemented");
#endif
}

void FileOps::removeFile(const char* src) throw (IOException)
{
#ifdef PLATFORM_UNIX
	if (unlink(src) != 0)
	{
		if (errno != ENOENT)
		{
			throw IOException("Unable to remove file " + std::string(src));
		}
	}
#else
	throw IOException("not implemented");
#endif
}

std::string FileOps::dirname(const char* path)
{
#ifdef PLATFORM_UNIX
	char* pathCopy = strdup(path);
	std::string dirname = ::dirname(pathCopy);
	free(pathCopy);
	return dirname;
#else
	throw IOException("not implemented");
#endif
}

void FileOps::touch(const char* path) throw (IOException)
{
#ifdef PLATFORM_UNIX
	// see http://pubs.opengroup.org/onlinepubs/9699919799/utilities/touch.html
	if (creat(path,S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH) != 0)
	{
		throw IOException("Unable to touch file " + std::string(path));
	}
#else
	throw IOException("not implemented");
#endif
}

<commit_msg>Fix FileOps::touch()<commit_after>#include "FileOps.h"

#include "Platform.h"
#include "StringUtils.h"

#include <malloc.h>
#include <string.h>
#include <fstream>

#include "minizip/unzip.h"

#ifdef PLATFORM_UNIX
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <libgen.h>
#endif

FileOps::IOException::IOException(const std::string& error)
: m_errno(0)
{
	m_error = error;

#ifdef PLATFORM_UNIX
	m_errno = errno;

	if (m_errno > 0)
	{
		m_error += " details: " + std::string(strerror(m_errno));
	}
#endif
}

FileOps::IOException::~IOException() throw ()
{
}

bool FileOps::fileExists(const char* path) throw (IOException)
{
#ifdef PLATFORM_UNIX
	struct stat fileInfo;
	if (stat(path,&fileInfo) != 0)
	{
		if (errno == ENOENT)
		{
			return false;
		}
		else
		{
			throw IOException("Error checking for file " + std::string(path));
		}
	}
	return true;
#else
	throw IOException("not implemented");
#endif
}

void FileOps::setPermissions(const char* path, int permissions) throw (IOException)
{
#ifdef PLATFORM_UNIX
	// TODO - Convert permissions correctly
	int mode = permissions;
	if (chmod(path,mode) != 0)
	{
		throw IOException("Failed to set permissions on " + std::string(path) + " to " + intToStr(permissions));
	}
#else
	throw IOException("not implemented");
#endif
}

void FileOps::moveFile(const char* src, const char* dest) throw (IOException)
{
#ifdef PLATFORM_UNIX
	if (rename(src,dest) != 0)
	{
		throw IOException("Unable to rename " + std::string(src) + " to " + std::string(dest));
	}
#else
	throw IOException("not implemented");
#endif
}

void FileOps::extractFromZip(const char* zipFilePath, const char* src, const char* dest) throw (IOException)
{
	unzFile zipFile = unzOpen(zipFilePath);
	int result = unzLocateFile(zipFile,src,0);
	if (result == UNZ_OK)
	{
		// found a match which is now the current file
		unzOpenCurrentFile(zipFile);
		int chunkSize = 4096;
		char buffer[chunkSize];

		std::ofstream outputFile(dest,std::ofstream::binary);
		if (!outputFile.good())
		{
			throw IOException("Unable to write to file " + std::string(src));
		}
		while (true)
		{
			int count = unzReadCurrentFile(zipFile,buffer,chunkSize);
			if (count <= 0)
			{
				if (count < 0)
				{
					throw IOException("Error extracting file from archive " + std::string(src));
				}
				break;
			}
			outputFile.write(buffer,count);
		}
		outputFile.close();

		unzCloseCurrentFile(zipFile);
	}
	else
	{
		throw IOException("Unable to find file " + std::string(src) + " in zip archive " + std::string(zipFilePath));
	}
	unzClose(zipFile);
}

void FileOps::mkdir(const char* dir) throw (IOException)
{
#ifdef PLATFORM_UNIX
	if (::mkdir(dir,S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) != 0)
	{
		throw IOException("Unable to create directory " + std::string(dir));
	}
#else
	throw IOException("not implemented");
#endif
}

void FileOps::rmdir(const char* dir) throw (IOException)
{
#ifdef PLATFORM_UNIX
	if (::rmdir(dir) != 0)
	{
		throw IOException("Unable to remove directory " + std::string(dir));
	}
#else
	throw IOException("not implemented");
#endif
}

void FileOps::createSymLink(const char* link, const char* target) throw (IOException)
{
#ifdef PLATFORM_UNIX
	if (symlink(target,link) != 0)
	{
		throw IOException("Unable to create symlink " + std::string(link) + " to " + std::string(target));
	}
#else
	// symlinks are not supported under Windows (at least, not universally.
	// Windows Vista and later do actually support symlinks)
	throw IOException("not implemented");
#endif
}

void FileOps::removeFile(const char* src) throw (IOException)
{
#ifdef PLATFORM_UNIX
	if (unlink(src) != 0)
	{
		if (errno != ENOENT)
		{
			throw IOException("Unable to remove file " + std::string(src));
		}
	}
#else
	throw IOException("not implemented");
#endif
}

std::string FileOps::dirname(const char* path)
{
#ifdef PLATFORM_UNIX
	char* pathCopy = strdup(path);
	std::string dirname = ::dirname(pathCopy);
	free(pathCopy);
	return dirname;
#else
	throw IOException("not implemented");
#endif
}

void FileOps::touch(const char* path) throw (IOException)
{
#ifdef PLATFORM_UNIX
	// see http://pubs.opengroup.org/onlinepubs/9699919799/utilities/touch.html
	int fd = creat(path,S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
	if (fd != -1)
	{
		close(fd);
	}
	else
	{
		throw IOException("Unable to touch file " + std::string(path));
	}
#else
	throw IOException("not implemented");
#endif
}

<|endoftext|>