<commit_before>/* bzflag
 * Copyright (c) 1993 - 2005 Tim Riker
 *
 * This package is free software;  you can redistribute it and/or
 * modify it under the terms of the license found in the file
 * named LICENSE that should have accompanied this file.
 *
 * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */

// Provide BZFS with a list server connection

/* class header */
#include "ListServerConnection.h"

/* system implementation headers */
#include <string.h>
#include <string>
#include <math.h>
#include <errno.h>
#include <set>

/* common implementation headers */
#include "bzfio.h"
#include "version.h"
#include "TextUtils.h"
#include "Protocol.h"
#include "GameKeeper.h"
#include "bzfsAPI.h"
#include "WorldEventManager.h"

/* local implementation headers */
#include "CmdLineOptions.h"

// FIXME remove externs!
extern PingPacket getTeamCounts();
extern uint16_t curMaxPlayers;
extern void sendMessage(int playerIndex, PlayerId targetPlayer,
			const char *message);
extern CmdLineOptions *clOptions;

const int ListServerLink::NotConnected = -1;

ListServerLink::ListServerLink(std::string listServerURL,
			       std::string publicizedAddress,
			       std::string publicizedTitle,
             std::string _advertiseGroups)
{

  std::string bzfsUserAgent = "bzfs ";
  bzfsUserAgent            += getAppVersion();

  setURL(listServerURL);
  setUserAgent(bzfsUserAgent);
  setDNSCachingTime(-1);
  setTimeout(10);

  if (clOptions->pingInterface != "")
    setInterface(clOptions->pingInterface);

  publicizeAddress     = publicizedAddress;
  publicizeDescription = publicizedTitle;
  advertiseGroups      = _advertiseGroups;

  //if this c'tor is called, it's safe to publicize
  publicizeServer      = true;
  queuedRequest        = false;
  // schedule initial ADD message
  queueMessage(ListServerLink::ADD);
}

ListServerLink::ListServerLink()
{
  // does not create a usable link, so checks should be placed
  // in  all public member functions to ensure that nothing tries
  // to happen if publicizeServer is false
  publicizeServer = false;
}

ListServerLink::~ListServerLink()
{
  // now tell the list server that we're going away.  this can
  // take some time but we don't want to wait too long.  we do
  // our own multiplexing loop and wait for a maximum of 3 seconds
  // total.

  // if we aren't supposed to be publicizing, skip the whole thing
  // and don't waste 3 seconds.
  if (!publicizeServer)
    return;

  queueMessage(ListServerLink::REMOVE);
  for (int i = 0; i < 12; i++) {
    cURLManager::perform();
    if (!queuedRequest)
      break;
    TimeKeeper::sleep(0.25f);
  }
}

void ListServerLink::finalization(char *data, unsigned int length, bool good)
{
  queuedRequest = false;
  if (good && (length < 2048)) {
    char buf[2048];
    memcpy(buf, data, length);
    int bytes = length;
    buf[bytes]=0;
    char* base = buf;
    static char *tokGoodIdentifier = "TOKGOOD: ";
    static char *tokBadIdentifier = "TOKBAD: ";
    // walks entire reply including HTTP headers
    while (*base) {
      // find next newline
      char* scan = base;
      while (*scan && *scan != '\r' && *scan != '\n') scan++;
      // if no newline then no more complete replies
      if (*scan != '\r' && *scan != '\n') break;
      while (*scan && (*scan == '\r' || *scan == '\n')) *scan++ = '\0';
      DEBUG4("Got line: \"%s\"\n", base);
      // TODO don't do this if we don't want central logins
      if (strncmp(base, tokGoodIdentifier, strlen(tokGoodIdentifier)) == 0) {
	char *callsign, *group;
	callsign = base + strlen(tokGoodIdentifier);
	DEBUG3("Got: %s\n", base);
	group = callsign;
	while (*group && (*group != ':')) group++;
	while (*group && (*group == ':')) *group++ = 0;
	int playerIndex;
	GameKeeper::Player *playerData = NULL;
	for (playerIndex = 0; playerIndex < curMaxPlayers; playerIndex++) {
	  playerData = GameKeeper::Player::getPlayerByIndex(playerIndex);
	  if (!playerData)
	    continue;
	  if (playerData->_LSAState != GameKeeper::Player::checking)
	    continue;
	  if (!TextUtils::compare_nocase(playerData->player.getCallSign(),
					 callsign))
	    break;
	}
	if (playerIndex < curMaxPlayers) {
	  // don't accept the global auth if there's a local account
	  // of the same name and the local account is not marked as
	  // being the same as the global account
	  if (!playerData->accessInfo.hasRealPassword()
              || playerData->accessInfo.getUserInfo(callsign)
	      .hasGroup("LOCAL.GLOBAL")) {
	    playerData->_LSAState = GameKeeper::Player::verified;
	    if (!playerData->accessInfo.isRegistered())
	      playerData->accessInfo.storeInfo(NULL);
	    playerData->accessInfo.setPermissionRights();
	    while (*group) {
              char *nextgroup = group;
	      while (*nextgroup && (*nextgroup != ':')) nextgroup++;
	      while (*nextgroup && (*nextgroup == ':')) *nextgroup++ = 0;
	      playerData->accessInfo.addGroup(group);
	      group = nextgroup;
	    }
	    playerData->authentication.global(true);
	    sendMessage(ServerPlayer, playerIndex, "Global login approved!");
	  } else {
	    playerData->_LSAState = GameKeeper::Player::failed;
	    sendMessage(ServerPlayer, playerIndex, "Global login rejected. "
	                "This callsign is registered locally on this "
			"server.");
	    sendMessage(ServerPlayer, playerIndex,
			"If the local account is yours, "
			"/identify, /deregister and reconnnect, "
			"or ask an admin for the LOCAL.GLOBAL group.");
	    sendMessage(ServerPlayer, playerIndex,
			"If it is not yours, please ask an admin "
			"to deregister it so that you may use your global "
			"callsign.");
	  }
	  playerData->player.clearToken();
	}
      } else if (!strncmp(base, tokBadIdentifier, strlen(tokBadIdentifier))) {
	char *callsign;
	callsign = base + strlen(tokBadIdentifier);
	int playerIndex;
	GameKeeper::Player *playerData = NULL;
	for (playerIndex = 0; playerIndex < curMaxPlayers; playerIndex++) {
	  playerData = GameKeeper::Player::getPlayerByIndex(playerIndex);
	  if (!playerData)
	    continue;
	  if (playerData->_LSAState != GameKeeper::Player::checking)
	    continue;
	  if (!TextUtils::compare_nocase(playerData->player.getCallSign(),
					 callsign))
	    break;
	}
	DEBUG3("Got: [%d] %s\n", playerIndex, base);
	if (playerIndex < curMaxPlayers) {
	  playerData->_LSAState = GameKeeper::Player::failed;
	  sendMessage(ServerPlayer, playerIndex,
		      "Global login rejected, bad token.");
	  playerData->player.clearToken();
	}
      }

      // next reply
      base = scan;
    }
  }
  for (int i = 0; i < curMaxPlayers; i++) {
    GameKeeper::Player *playerData = GameKeeper::Player::getPlayerByIndex(i);
    if (!playerData)
      continue;
    if (playerData->_LSAState != GameKeeper::Player::checking)
      continue;
    playerData->_LSAState = GameKeeper::Player::timed;
  }
  if (nextMessageType != ListServerLink::NONE) {
    // There was a pending request arrived after we write:
    // we should redo all the stuff
    sendQueuedMessages();
  }
}

void ListServerLink::queueMessage(MessageType type)
{
  // ignore if the server is not public
  if (!publicizeServer) return;

  // record next message to send.
  nextMessageType = type;

  if (!queuedRequest)
    sendQueuedMessages();
  else
    DEBUG3("There is a message already queued to the list server: not sending this one yet.\n");
}

void ListServerLink::sendQueuedMessages()
{
  queuedRequest = true;
  if (nextMessageType == ListServerLink::ADD) {
    DEBUG3("Queuing ADD message to list server\n");

	bz_ListServerUpdateEvent	updateEvent;
	updateEvent.address = publicizeAddress;
	updateEvent.description = publicizeDescription;
	updateEvent.groups = advertiseGroups;

	worldEventManager.callEvents(bz_eListServerUpdateEvent,&updateEvent);

	addMe(getTeamCounts(), std::string(updateEvent.address.c_str()), std::string(updateEvent.description.c_str()), std::string(updateEvent.groups.c_str()));
    lastAddTime = TimeKeeper::getCurrent();
  } else if (nextMessageType == ListServerLink::REMOVE) {
    DEBUG3("Queuing REMOVE message to list server\n");
    removeMe(publicizeAddress);
  }
  nextMessageType = ListServerLink::NONE;
}

void ListServerLink::addMe(PingPacket pingInfo,
			   std::string publicizedAddress,
			   std::string publicizedTitle,
         std::string _advertiseGroups)
{
  std::string msg;
  std::string hdr;

  // encode ping reply as ascii hex digits plus NULL
  char gameInfo[PingPacketHexPackedSize + 1];
  pingInfo.packHex(gameInfo);

  // send ADD message (must send blank line)
  msg  = "action=ADD&nameport=";
  msg += publicizedAddress;
  msg += "&version=";
  msg += getServerVersion();
  msg += "&gameinfo=";
  msg += gameInfo;
  msg += "&build=";
  msg += getAppVersion();
  msg += "&checktokens=";

  std::set<std::string> callSigns;
  // callsign1@ip1=token1%0D%0Acallsign2@ip2=token2%0D%0A
  for (int i = 0; i < curMaxPlayers; i++) {
    GameKeeper::Player *playerData = GameKeeper::Player::getPlayerByIndex(i);
    if (!playerData)
      continue;
    if ((playerData->_LSAState != GameKeeper::Player::required)
	&& (playerData->_LSAState != GameKeeper::Player::requesting))
      continue;
    if (callSigns.count(playerData->player.getCallSign()))
      continue;
    callSigns.insert(playerData->player.getCallSign());
    playerData->_LSAState = GameKeeper::Player::checking;
    NetHandler *handler = playerData->netHandler;
    msg += TextUtils::url_encode(playerData->player.getCallSign());
    Address addr = handler->getIPAddress();
    if (!addr.isPrivate()) {
	msg += "@";
	msg += handler->getTargetIP();
    }
    msg += "=";
    msg += playerData->player.getToken();
    msg += "%0D%0A";
  }

  msg += "&groups=";
  // *groups=GROUP0%0D%0AGROUP1%0D%0A
  PlayerAccessMap::iterator itr = groupAccess.begin();
  for ( ; itr != groupAccess.end(); itr++) {
    if (itr->first.substr(0, 6) != "LOCAL.") {
      msg += itr->first.c_str();
      msg += "%0D%0A";
    }
  }

  msg += "&advertgroups=";
  msg += _advertiseGroups;
  msg += "&title=";
  msg += publicizedTitle;

  setPostMode(msg);
  addHandle();
}

void ListServerLink::removeMe(std::string publicizedAddress)
{
  std::string msg;

  msg  = "action=REMOVE&nameport=";
  msg += publicizedAddress;

  setPostMode(msg);
  addHandle();
}

// Local Variables: ***
// mode:C++ ***
// tab-width: 8 ***
// c-basic-offset: 2 ***
// indent-tabs-mode: t ***
// End: ***
// ex: shiftwidth=2 tabstop=8
<commit_msg>ws<commit_after>/* bzflag
 * Copyright (c) 1993 - 2005 Tim Riker
 *
 * This package is free software;  you can redistribute it and/or
 * modify it under the terms of the license found in the file
 * named LICENSE that should have accompanied this file.
 *
 * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */

// Provide BZFS with a list server connection

/* class header */
#include "ListServerConnection.h"

/* system implementation headers */
#include <string.h>
#include <string>
#include <math.h>
#include <errno.h>
#include <set>

/* common implementation headers */
#include "bzfio.h"
#include "version.h"
#include "TextUtils.h"
#include "Protocol.h"
#include "GameKeeper.h"
#include "bzfsAPI.h"
#include "WorldEventManager.h"

/* local implementation headers */
#include "CmdLineOptions.h"

// FIXME remove externs!
extern PingPacket getTeamCounts();
extern uint16_t curMaxPlayers;
extern void sendMessage(int playerIndex, PlayerId targetPlayer,
			const char *message);
extern CmdLineOptions *clOptions;

const int ListServerLink::NotConnected = -1;

ListServerLink::ListServerLink(std::string listServerURL,
			       std::string publicizedAddress,
			       std::string publicizedTitle,
			       std::string _advertiseGroups)
{

  std::string bzfsUserAgent = "bzfs ";
  bzfsUserAgent            += getAppVersion();

  setURL(listServerURL);
  setUserAgent(bzfsUserAgent);
  setDNSCachingTime(-1);
  setTimeout(10);

  if (clOptions->pingInterface != "")
    setInterface(clOptions->pingInterface);

  publicizeAddress     = publicizedAddress;
  publicizeDescription = publicizedTitle;
  advertiseGroups      = _advertiseGroups;

  //if this c'tor is called, it's safe to publicize
  publicizeServer      = true;
  queuedRequest        = false;
  // schedule initial ADD message
  queueMessage(ListServerLink::ADD);
}

ListServerLink::ListServerLink()
{
  // does not create a usable link, so checks should be placed
  // in  all public member functions to ensure that nothing tries
  // to happen if publicizeServer is false
  publicizeServer = false;
}

ListServerLink::~ListServerLink()
{
  // now tell the list server that we're going away.  this can
  // take some time but we don't want to wait too long.  we do
  // our own multiplexing loop and wait for a maximum of 3 seconds
  // total.

  // if we aren't supposed to be publicizing, skip the whole thing
  // and don't waste 3 seconds.
  if (!publicizeServer)
    return;

  queueMessage(ListServerLink::REMOVE);
  for (int i = 0; i < 12; i++) {
    cURLManager::perform();
    if (!queuedRequest)
      break;
    TimeKeeper::sleep(0.25f);
  }
}

void ListServerLink::finalization(char *data, unsigned int length, bool good)
{
  queuedRequest = false;
  if (good && (length < 2048)) {
    char buf[2048];
    memcpy(buf, data, length);
    int bytes = length;
    buf[bytes]=0;
    char* base = buf;
    static char *tokGoodIdentifier = "TOKGOOD: ";
    static char *tokBadIdentifier = "TOKBAD: ";
    // walks entire reply including HTTP headers
    while (*base) {
      // find next newline
      char* scan = base;
      while (*scan && *scan != '\r' && *scan != '\n') scan++;
      // if no newline then no more complete replies
      if (*scan != '\r' && *scan != '\n') break;
      while (*scan && (*scan == '\r' || *scan == '\n')) *scan++ = '\0';
      DEBUG4("Got line: \"%s\"\n", base);
      // TODO don't do this if we don't want central logins
      if (strncmp(base, tokGoodIdentifier, strlen(tokGoodIdentifier)) == 0) {
	char *callsign, *group;
	callsign = base + strlen(tokGoodIdentifier);
	DEBUG3("Got: %s\n", base);
	group = callsign;
	while (*group && (*group != ':')) group++;
	while (*group && (*group == ':')) *group++ = 0;
	int playerIndex;
	GameKeeper::Player *playerData = NULL;
	for (playerIndex = 0; playerIndex < curMaxPlayers; playerIndex++) {
	  playerData = GameKeeper::Player::getPlayerByIndex(playerIndex);
	  if (!playerData)
	    continue;
	  if (playerData->_LSAState != GameKeeper::Player::checking)
	    continue;
	  if (!TextUtils::compare_nocase(playerData->player.getCallSign(),
					 callsign))
	    break;
	}
	if (playerIndex < curMaxPlayers) {
	  // don't accept the global auth if there's a local account
	  // of the same name and the local account is not marked as
	  // being the same as the global account
	  if (!playerData->accessInfo.hasRealPassword()
              || playerData->accessInfo.getUserInfo(callsign)
	      .hasGroup("LOCAL.GLOBAL")) {
	    playerData->_LSAState = GameKeeper::Player::verified;
	    if (!playerData->accessInfo.isRegistered())
	      playerData->accessInfo.storeInfo(NULL);
	    playerData->accessInfo.setPermissionRights();
	    while (*group) {
              char *nextgroup = group;
	      while (*nextgroup && (*nextgroup != ':')) nextgroup++;
	      while (*nextgroup && (*nextgroup == ':')) *nextgroup++ = 0;
	      playerData->accessInfo.addGroup(group);
	      group = nextgroup;
	    }
	    playerData->authentication.global(true);
	    sendMessage(ServerPlayer, playerIndex, "Global login approved!");
	  } else {
	    playerData->_LSAState = GameKeeper::Player::failed;
	    sendMessage(ServerPlayer, playerIndex, "Global login rejected. "
	                "This callsign is registered locally on this "
			"server.");
	    sendMessage(ServerPlayer, playerIndex,
			"If the local account is yours, "
			"/identify, /deregister and reconnnect, "
			"or ask an admin for the LOCAL.GLOBAL group.");
	    sendMessage(ServerPlayer, playerIndex,
			"If it is not yours, please ask an admin "
			"to deregister it so that you may use your global "
			"callsign.");
	  }
	  playerData->player.clearToken();
	}
      } else if (!strncmp(base, tokBadIdentifier, strlen(tokBadIdentifier))) {
	char *callsign;
	callsign = base + strlen(tokBadIdentifier);
	int playerIndex;
	GameKeeper::Player *playerData = NULL;
	for (playerIndex = 0; playerIndex < curMaxPlayers; playerIndex++) {
	  playerData = GameKeeper::Player::getPlayerByIndex(playerIndex);
	  if (!playerData)
	    continue;
	  if (playerData->_LSAState != GameKeeper::Player::checking)
	    continue;
	  if (!TextUtils::compare_nocase(playerData->player.getCallSign(),
					 callsign))
	    break;
	}
	DEBUG3("Got: [%d] %s\n", playerIndex, base);
	if (playerIndex < curMaxPlayers) {
	  playerData->_LSAState = GameKeeper::Player::failed;
	  sendMessage(ServerPlayer, playerIndex,
		      "Global login rejected, bad token.");
	  playerData->player.clearToken();
	}
      }

      // next reply
      base = scan;
    }
  }
  for (int i = 0; i < curMaxPlayers; i++) {
    GameKeeper::Player *playerData = GameKeeper::Player::getPlayerByIndex(i);
    if (!playerData)
      continue;
    if (playerData->_LSAState != GameKeeper::Player::checking)
      continue;
    playerData->_LSAState = GameKeeper::Player::timed;
  }
  if (nextMessageType != ListServerLink::NONE) {
    // There was a pending request arrived after we write:
    // we should redo all the stuff
    sendQueuedMessages();
  }
}

void ListServerLink::queueMessage(MessageType type)
{
  // ignore if the server is not public
  if (!publicizeServer) return;

  // record next message to send.
  nextMessageType = type;

  if (!queuedRequest)
    sendQueuedMessages();
  else
    DEBUG3("There is a message already queued to the list server: not sending this one yet.\n");
}

void ListServerLink::sendQueuedMessages()
{
  queuedRequest = true;
  if (nextMessageType == ListServerLink::ADD) {
    DEBUG3("Queuing ADD message to list server\n");

	bz_ListServerUpdateEvent	updateEvent;
	updateEvent.address = publicizeAddress;
	updateEvent.description = publicizeDescription;
	updateEvent.groups = advertiseGroups;

	worldEventManager.callEvents(bz_eListServerUpdateEvent,&updateEvent);

	addMe(getTeamCounts(), std::string(updateEvent.address.c_str()), std::string(updateEvent.description.c_str()), std::string(updateEvent.groups.c_str()));
    lastAddTime = TimeKeeper::getCurrent();
  } else if (nextMessageType == ListServerLink::REMOVE) {
    DEBUG3("Queuing REMOVE message to list server\n");
    removeMe(publicizeAddress);
  }
  nextMessageType = ListServerLink::NONE;
}

void ListServerLink::addMe(PingPacket pingInfo,
			   std::string publicizedAddress,
			   std::string publicizedTitle,
			   std::string _advertiseGroups)
{
  std::string msg;
  std::string hdr;

  // encode ping reply as ascii hex digits plus NULL
  char gameInfo[PingPacketHexPackedSize + 1];
  pingInfo.packHex(gameInfo);

  // send ADD message (must send blank line)
  msg  = "action=ADD&nameport=";
  msg += publicizedAddress;
  msg += "&version=";
  msg += getServerVersion();
  msg += "&gameinfo=";
  msg += gameInfo;
  msg += "&build=";
  msg += getAppVersion();
  msg += "&checktokens=";

  std::set<std::string> callSigns;
  // callsign1@ip1=token1%0D%0Acallsign2@ip2=token2%0D%0A
  for (int i = 0; i < curMaxPlayers; i++) {
    GameKeeper::Player *playerData = GameKeeper::Player::getPlayerByIndex(i);
    if (!playerData)
      continue;
    if ((playerData->_LSAState != GameKeeper::Player::required)
	&& (playerData->_LSAState != GameKeeper::Player::requesting))
      continue;
    if (callSigns.count(playerData->player.getCallSign()))
      continue;
    callSigns.insert(playerData->player.getCallSign());
    playerData->_LSAState = GameKeeper::Player::checking;
    NetHandler *handler = playerData->netHandler;
    msg += TextUtils::url_encode(playerData->player.getCallSign());
    Address addr = handler->getIPAddress();
    if (!addr.isPrivate()) {
	msg += "@";
	msg += handler->getTargetIP();
    }
    msg += "=";
    msg += playerData->player.getToken();
    msg += "%0D%0A";
  }

  msg += "&groups=";
  // *groups=GROUP0%0D%0AGROUP1%0D%0A
  PlayerAccessMap::iterator itr = groupAccess.begin();
  for ( ; itr != groupAccess.end(); itr++) {
    if (itr->first.substr(0, 6) != "LOCAL.") {
      msg += itr->first.c_str();
      msg += "%0D%0A";
    }
  }

  msg += "&advertgroups=";
  msg += _advertiseGroups;
  msg += "&title=";
  msg += publicizedTitle;

  setPostMode(msg);
  addHandle();
}

void ListServerLink::removeMe(std::string publicizedAddress)
{
  std::string msg;

  msg  = "action=REMOVE&nameport=";
  msg += publicizedAddress;

  setPostMode(msg);
  addHandle();
}

// Local Variables: ***
// mode:C++ ***
// tab-width: 8 ***
// c-basic-offset: 2 ***
// indent-tabs-mode: t ***
// End: ***
// ex: shiftwidth=2 tabstop=8
<|endoftext|>


<commit_before>////////////////////////////////////////////////////////////////////////////////
// taskwarrior - a command line task list manager.
//
// Copyright 2006 - 2011, Paul Beckingham, Federico Hernandez.
// All rights reserved.
//
// This program is free software; you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free Software
// Foundation; either version 2 of the License, or (at your option) any later
// version.
//
// This program 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 General Public License for more
// details.
//
// You should have received a copy of the GNU General Public License along with
// this program; if not, write to the
//
//     Free Software Foundation, Inc.,
//     51 Franklin Street, Fifth Floor,
//     Boston, MA
//     02110-1301
//     USA
//
////////////////////////////////////////////////////////////////////////////////

#include <algorithm>
#include <Context.h>
#include <ColDepends.h>
#include <text.h>
#include <main.h>

extern Context context;

////////////////////////////////////////////////////////////////////////////////
ColumnDepends::ColumnDepends ()
{
  _type  = "string";
  _style = "default";
  _label = "Depends";
}

////////////////////////////////////////////////////////////////////////////////
ColumnDepends::~ColumnDepends ()
{
}

////////////////////////////////////////////////////////////////////////////////
// Overriden so that style <----> label are linked.
// Note that you can not determine which gets called first.
void ColumnDepends::setStyle (const std::string& value)
{
  _style = value;

       if (_style == "indicator" && _label == "Depends") _label = _label.substr (0, context.config.get ("dependency.indicator").length ());
  else if (_style == "count"     && _label == "Depends") _label = "Dep";
}

////////////////////////////////////////////////////////////////////////////////
// Set the minimum and maximum widths for the value.
void ColumnDepends::measure (Task& task, int& minimum, int& maximum)
{
  std::vector <Task> blocked;
  dependencyGetBlocking (task, blocked);

       if (_style == "indicator") minimum = maximum = context.config.get ("dependency.indicator").length ();
  else if (_style == "count")     minimum = maximum = 2 + format ((int) blocked.size ()).length ();
  else if (_style == "default")
  {
    minimum = maximum = 0;
    if (task.has ("depends"))
    {
      std::vector <int> blocked_ids;
      std::vector <Task>::iterator i;
      for (i = blocked.begin (); i != blocked.end (); ++i)
        blocked_ids.push_back (i->id);

      std::string all;
      join (all, " ", blocked_ids);
      maximum = all.length ();

      int length;
      for (i = blocked.begin (); i != blocked.end (); ++i)
      {
        length = format (i->id).length ();
        if (length > minimum)
          minimum = length;
      }
    }
  }
  else
    throw std::string ("Unrecognized column format 'depends.") + _style + "'";
}

////////////////////////////////////////////////////////////////////////////////
void ColumnDepends::render (
  std::vector <std::string>& lines,
  Task& task,
  int width,
  Color& color)
{
  if (task.has ("depends"))
  {
    if (_style == "indicator")
    {
      lines.push_back (
        color.colorize (
          rightJustify (context.config.get ("dependency.indicator"), width)));
      return;
    }

    std::vector <Task> blocked;
    dependencyGetBlocking (task, blocked);

    if (_style == "count")
    {
      lines.push_back (
        color.colorize (
          rightJustify ("[" + format ((int)blocked.size ()) + "]", width)));
    }
    else if (_style == "default")
    {
      std::vector <Task> blocked;
      dependencyGetBlocking (task, blocked);

      std::string combined;
      std::vector <int> blocked_ids;
      join (combined, " ", blocked_ids);

      std::vector <std::string> all;
      wrapText (all, combined, width);

      std::vector <std::string>::iterator i;
      for (i = all.begin (); i != all.end (); ++i)
        lines.push_back (color.colorize (leftJustify (*i, width)));
    }
  }
}

////////////////////////////////////////////////////////////////////////////////
<commit_msg>Bug<commit_after>////////////////////////////////////////////////////////////////////////////////
// taskwarrior - a command line task list manager.
//
// Copyright 2006 - 2011, Paul Beckingham, Federico Hernandez.
// All rights reserved.
//
// This program is free software; you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free Software
// Foundation; either version 2 of the License, or (at your option) any later
// version.
//
// This program 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 General Public License for more
// details.
//
// You should have received a copy of the GNU General Public License along with
// this program; if not, write to the
//
//     Free Software Foundation, Inc.,
//     51 Franklin Street, Fifth Floor,
//     Boston, MA
//     02110-1301
//     USA
//
////////////////////////////////////////////////////////////////////////////////

#include <algorithm>
#include <Context.h>
#include <ColDepends.h>
#include <text.h>
#include <main.h>

extern Context context;

////////////////////////////////////////////////////////////////////////////////
ColumnDepends::ColumnDepends ()
{
  _type  = "string";
  _style = "default";
  _label = "Depends";
}

////////////////////////////////////////////////////////////////////////////////
ColumnDepends::~ColumnDepends ()
{
}

////////////////////////////////////////////////////////////////////////////////
// Overriden so that style <----> label are linked.
// Note that you can not determine which gets called first.
void ColumnDepends::setStyle (const std::string& value)
{
  _style = value;

       if (_style == "indicator" && _label == "Depends") _label = _label.substr (0, context.config.get ("dependency.indicator").length ());
  else if (_style == "count"     && _label == "Depends") _label = "Dep";
}

////////////////////////////////////////////////////////////////////////////////
// Set the minimum and maximum widths for the value.
void ColumnDepends::measure (Task& task, int& minimum, int& maximum)
{
  std::vector <Task> blocking;
  dependencyGetBlocking (task, blocking);

       if (_style == "indicator") minimum = maximum = context.config.get ("dependency.indicator").length ();
  else if (_style == "count")     minimum = maximum = 2 + format ((int) blocking.size ()).length ();
  else if (_style == "default")
  {
    minimum = maximum = 0;
    if (task.has ("depends"))
    {
      std::vector <int> blocking_ids;
      std::vector <Task>::iterator i;
      for (i = blocking.begin (); i != blocking.end (); ++i)
        blocking_ids.push_back (i->id);

      std::string all;
      join (all, " ", blocking_ids);
      maximum = all.length ();

      int length;
      for (i = blocking.begin (); i != blocking.end (); ++i)
      {
        length = format (i->id).length ();
        if (length > minimum)
          minimum = length;
      }
    }
  }
  else
    throw std::string ("Unrecognized column format 'depends.") + _style + "'";
}

////////////////////////////////////////////////////////////////////////////////
void ColumnDepends::render (
  std::vector <std::string>& lines,
  Task& task,
  int width,
  Color& color)
{
  if (task.has ("depends"))
  {
    if (_style == "indicator")
    {
      lines.push_back (
        color.colorize (
          rightJustify (context.config.get ("dependency.indicator"), width)));
      return;
    }

    std::vector <Task> blocking;
    dependencyGetBlocking (task, blocking);

    if (_style == "count")
    {
      lines.push_back (
        color.colorize (
          rightJustify ("[" + format ((int)blocking.size ()) + "]", width)));
    }
    else if (_style == "default")
    {
      std::vector <int> blocking_ids;
      std::vector <Task>::iterator t;
      for (t = blocking.begin (); t != blocking.end (); ++t)
        blocking_ids.push_back (t->id);

      std::string combined;
      join (combined, " ", blocking_ids);

      std::vector <std::string> all;
      wrapText (all, combined, width);

      std::vector <std::string>::iterator i;
      for (i = all.begin (); i != all.end (); ++i)
        lines.push_back (color.colorize (leftJustify (*i, width)));
    }
  }
}

////////////////////////////////////////////////////////////////////////////////
<|endoftext|>


<commit_before>/*
Copyright (c) 2016 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.

Author: Leonardo de Moura
*/
#include "util/sstream.h"
#include "kernel/instantiate.h"
#include "kernel/inductive/inductive.h"
#include "library/util.h"
#include "library/constants.h"
#include "library/normalize.h"
#include "library/aux_recursors.h"
#include "compiler/util.h"
#include "compiler/comp_irrelevant.h"
#include "compiler/compiler_step_visitor.h"

namespace lean {
static expr * g_neutral_expr     = nullptr;
static expr * g_unreachable_expr = nullptr;
class erase_irrelevant_fn : public compiler_step_visitor {

    virtual expr visit_sort(expr const &) override {
        return *g_neutral_expr;
    }

    virtual expr visit_pi(expr const &) override {
        return *g_neutral_expr;
    }

    virtual expr visit_macro(expr const & e) override {
        if (is_marked_as_comp_irrelevant(e))
            return *g_neutral_expr;
        else if (is_comp_irrelevant(ctx(), e))
            return *g_neutral_expr;
        else
            return compiler_step_visitor::visit_macro(e);
    }

    virtual expr visit_local(expr const & e) override {
        if (is_comp_irrelevant(ctx(), e))
            return *g_neutral_expr;
        else
            return e;
    }

    virtual expr visit_constant(expr const & e) override {
        if (is_comp_irrelevant(ctx(), e)) {
            return *g_neutral_expr;
        } else {
            /* Erase universe level information */
            return mk_constant(const_name(e));
        }
    }

    /* Process minor premises and args, and distribute args over minors.
       The size of cnames is nminors, and it contains the names of the constructors. */
    void visit_minors(unsigned nparams, unsigned nminors, expr * minors, name const * cnames,
                      unsigned nargs, expr * args) {
        if (nargs > 0) {
            for (unsigned i = 0; i < nargs; i++)
                args[i] = visit(args[i]);
            /* distribute args over minors */
            for (unsigned i = 0; i < nminors; i++) {
                unsigned carity = get_constructor_arity(env(), cnames[i]);
                lean_assert(carity >= nparams);
                unsigned data_sz = carity - nparams;
                type_context::tmp_locals locals(ctx());
                expr new_minor   = minors[i];
                for (unsigned j = 0; j < data_sz; j++) {
                    if (!is_lambda(new_minor))
                        throw exception("unexpected occurrence of 'cases_on' expression, the minor premise is expected to be a lambda-expression");
                    expr local = locals.push_local_from_binding(new_minor);
                    new_minor  = instantiate(binding_body(new_minor), local);
                }
                new_minor = visit(new_minor);
                new_minor = beta_reduce(mk_app(new_minor, nargs, args));
                minors[i] = locals.mk_lambda(new_minor);
            }
        } else {
            for (unsigned i = 0; i < nminors; i++)
                minors[i] = visit(minors[i]);
        }
    }

    /* We keep only the major and minor premises in cases_on applications. */
    expr visit_cases_on(expr const & fn, buffer<expr> & args) {
        name const & rec_name       = const_name(fn);
        name const & I_name         = rec_name.get_prefix();
        unsigned nparams            = *inductive::get_num_params(env(), I_name);
        unsigned nminors            = *inductive::get_num_minor_premises(env(), I_name);
        unsigned nindices           = *inductive::get_num_indices(env(), I_name);
        unsigned arity              = nparams + 1 /* typeformer/motive */ + nindices + 1 /* major premise */ + nminors;
        lean_assert(args.size() >= arity);
        buffer<name> cnames;
        get_intro_rule_names(env(), I_name, cnames);
        expr * minors        = args.data() + nparams + 1 + nindices + 1;
        unsigned nextra_args = args.size() - arity;
        expr * extra_args    = args.data() + arity;
        visit_minors(nparams, nminors, minors, cnames.data(), nextra_args, extra_args);
        expr major  = visit(args[nparams + 1 + nindices]);
        expr new_fn = visit(fn);
        return mk_app(mk_app(new_fn, major), nminors, minors);
    }

    /* We keep only the major and minor premises in rec applications.
       This method also converts the rec into cases_on */
    expr visit_rec(expr const & fn, buffer<expr> & args) {
        name const & rec_name       = const_name(fn);
        name const & I_name         = rec_name.get_prefix();
        /* This preprocessing step assumes that recursive recursors have already been eliminated */
        lean_assert(!is_recursive_datatype(env(), I_name));
        unsigned nparams            = *inductive::get_num_params(env(), I_name);
        unsigned nminors            = *inductive::get_num_minor_premises(env(), I_name);
        unsigned nindices           = *inductive::get_num_indices(env(), I_name);
        unsigned arity              = nparams + 1 /* typeformer/motive */ + nminors + nindices + 1 /* major premise */;
        lean_assert(args.size() >= arity);
        buffer<name> cnames;
        get_intro_rule_names(env(), I_name, cnames);
        expr new_fn = mk_constant(name(I_name, "cases_on"));
        expr major  = visit(args[nparams + 1 + nminors + nindices]);
        expr * minors        = args.data() + nparams + 1;
        unsigned nextra_args = args.size() - arity;
        expr * extra_args    = args.data() + arity;
        visit_minors(nparams, nminors, minors, cnames.data(), nextra_args, extra_args);
        return mk_app(mk_app(new_fn, major), nminors, minors);
    }

    expr add_args(expr e, unsigned start_idx, buffer<expr> const & args) {
        for (unsigned i = start_idx; i < args.size(); i++)
            e = mk_app(e, args[i]);
        return beta_reduce(e);
    }

    /* Remove eq.rec applications since they are just "type-casting" operations. */
    expr visit_eq_rec(buffer<expr> & args) {
        lean_assert(args.size() >= 6);
        expr major = visit(args[3]);
        return add_args(major, 6, args);
    }

    expr consume_lambdas(type_context::tmp_locals & locals, expr e) {
        while (true) {
            if (is_lambda(e)) {
                expr local = locals.push_local_from_binding(e);
                e = instantiate(binding_body(e), local);
            } else {
                return beta_reduce(e);
            }
        }
    }

    /* We can eliminate no_confusion applications, they do not add any relevant information to the environment */
    expr visit_no_confusion(expr const & fn, buffer<expr> const & args) {
        lean_assert(is_constant(fn));
        name const & no_confusion_name  = const_name(fn);
        name const & I_name             = no_confusion_name.get_prefix();
        unsigned nparams                = *inductive::get_num_params(env(), I_name);
        unsigned nindices               = *inductive::get_num_indices(env(), I_name);
        DEBUG_CODE(unsigned basic_arity = nparams + nindices + 1 /* motive */ + 2 /* lhs/rhs */ + 1 /* equality */;);
        lean_assert(args.size() >= basic_arity);
        expr lhs                        = ctx().whnf(args[nparams + nindices + 1]);
        expr rhs                        = ctx().whnf(args[nparams + nindices + 2]);
        optional<name> lhs_constructor  = is_constructor_app(env(), lhs);
        optional<name> rhs_constructor  = is_constructor_app(env(), rhs);
        if (!lhs_constructor || !rhs_constructor)
            throw exception(sstream() << "code generation failed, unsupported occurrence of '" << no_confusion_name << "', constructors expected");
        if (lhs_constructor != rhs_constructor)
            return *g_unreachable_expr;
        lean_assert(args.size() >= basic_arity + 1);
        expr major = args[nparams + nindices + 4];
        type_context::tmp_locals locals(ctx());
        major = consume_lambdas(locals, major);
        major = visit(major);
        major = locals.mk_lambda(major);
        expr r = major;

        unsigned c_data_sz = get_constructor_arity(env(), *lhs_constructor) - nparams;
        for (unsigned i = 0; i < c_data_sz; i++)
            r = mk_app(r, *g_neutral_expr); // add dummy proofs
        /* add remaining arguments */
        return add_args(r, nparams + nindices + 5, args);
    }

    /* Treat subtype.tag as the identity function */
    virtual expr visit_subtype_tag(buffer<expr> const & args) {
        lean_assert(args.size() >= 4);
        expr r = visit(args[2]);
        return add_args(r, 4, args);
    }

    /* Eliminate subtype.rec */
    virtual expr visit_subtype_rec(buffer<expr> const & args) {
        lean_assert(args.size() >= 5);
        expr minor = visit(args[3]);
        expr major = visit(args[4]);
        expr r     = mk_app(minor, major, *g_neutral_expr);
        return add_args(r, 5, args);
    }

    /* subtype.elt_of is also compiled as the identity function */
    virtual expr visit_subtype_elt_of(buffer<expr> const & args) {
        lean_assert(args.size() >= 3);
        expr r = visit(args[2]);
        return add_args(r, 3, args);
    }

    virtual expr visit_app(expr const & e) override {
        if (is_comp_irrelevant(ctx(), e))
            return *g_neutral_expr;
        buffer<expr> args;
        expr const & fn = get_app_args(e, args);
        if (is_lambda(fn)) {
            return visit(beta_reduce(e));
        } else if (is_constant(fn)) {
            name const & n = const_name(fn);
            if (n == get_eq_rec_name()) {
                return visit_eq_rec(args);
            } else if (n == get_subtype_rec_name()) {
                return visit_subtype_rec(args);
            } else if (is_cases_on_recursor(env(), n)) {
                return visit_cases_on(fn, args);
            } else if (inductive::is_elim_rule(env(), n)) {
                return visit_rec(fn, args);
            } else if (is_no_confusion(env(), n)) {
                return visit_no_confusion(fn, args);
            } else if (n == get_subtype_tag_name()) {
                return visit_subtype_tag(args);
            } else if (n == get_subtype_elt_of_name()) {
                return visit_subtype_elt_of(args);
            }
        }
        return compiler_step_visitor::visit_app(e);
    }

public:
    erase_irrelevant_fn(environment const & env):compiler_step_visitor(env) {}
};

expr erase_irrelevant(environment const & env, expr const & e) {
    return erase_irrelevant_fn(env)(e);
}

bool is_neutral_expr(expr const & e) {
    return e == *g_neutral_expr;
}

bool is_unreachable_expr(expr const & e) {
    return e == *g_unreachable_expr;
}

void initialize_erase_irrelevant() {
    g_neutral_expr     = new expr(mk_constant("_neutral_"));
    g_unreachable_expr = new expr(mk_constant("_unreachable_"));
}

void finalize_erase_irrelevant() {
    delete g_neutral_expr;
    delete g_unreachable_expr;
}
}
<commit_msg>feat(compiler/erase_irrelevant): erase type information from lambda and let expressions<commit_after>/*
Copyright (c) 2016 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.

Author: Leonardo de Moura
*/
#include "util/sstream.h"
#include "kernel/instantiate.h"
#include "kernel/inductive/inductive.h"
#include "library/util.h"
#include "library/constants.h"
#include "library/normalize.h"
#include "library/aux_recursors.h"
#include "compiler/util.h"
#include "compiler/comp_irrelevant.h"
#include "compiler/compiler_step_visitor.h"

namespace lean {
static expr * g_neutral_expr     = nullptr;
static expr * g_unreachable_expr = nullptr;
class erase_irrelevant_fn : public compiler_step_visitor {

    virtual expr visit_sort(expr const &) override {
        return *g_neutral_expr;
    }

    virtual expr visit_pi(expr const &) override {
        return *g_neutral_expr;
    }

    virtual expr visit_macro(expr const & e) override {
        if (is_marked_as_comp_irrelevant(e))
            return *g_neutral_expr;
        else if (is_comp_irrelevant(ctx(), e))
            return *g_neutral_expr;
        else
            return compiler_step_visitor::visit_macro(e);
    }

    virtual expr visit_local(expr const & e) override {
        if (is_comp_irrelevant(ctx(), e))
            return *g_neutral_expr;
        else
            return e;
    }

    virtual expr visit_constant(expr const & e) override {
        if (is_comp_irrelevant(ctx(), e)) {
            return *g_neutral_expr;
        } else {
            /* Erase universe level information */
            return mk_constant(const_name(e));
        }
    }

    expr erase_lambda_let_types(expr const & e) {
        if (is_lambda(e)) {
            return mk_lambda(binding_name(e), *g_neutral_expr, erase_lambda_let_types(binding_body(e)));
        } else if (is_let(e)) {
            return mk_let(let_name(e), *g_neutral_expr, let_value(e), erase_lambda_let_types(let_body(e)));
        } else {
            return e;
        }
    }

    virtual expr visit_lambda(expr const & e) override {
        return erase_lambda_let_types(compiler_step_visitor::visit_lambda(e));
    }

    virtual expr visit_let(expr const & e) override {
        return erase_lambda_let_types(compiler_step_visitor::visit_let(e));
    }

    /* Process minor premises and args, and distribute args over minors.
       The size of cnames is nminors, and it contains the names of the constructors. */
    void visit_minors(unsigned nparams, unsigned nminors, expr * minors, name const * cnames,
                      unsigned nargs, expr * args) {
        if (nargs > 0) {
            for (unsigned i = 0; i < nargs; i++)
                args[i] = visit(args[i]);
            /* distribute args over minors */
            for (unsigned i = 0; i < nminors; i++) {
                unsigned carity = get_constructor_arity(env(), cnames[i]);
                lean_assert(carity >= nparams);
                unsigned data_sz = carity - nparams;
                type_context::tmp_locals locals(ctx());
                expr new_minor   = minors[i];
                for (unsigned j = 0; j < data_sz; j++) {
                    if (!is_lambda(new_minor))
                        throw exception("unexpected occurrence of 'cases_on' expression, the minor premise is expected to be a lambda-expression");
                    expr local = locals.push_local_from_binding(new_minor);
                    new_minor  = instantiate(binding_body(new_minor), local);
                }
                new_minor = visit(new_minor);
                new_minor = beta_reduce(mk_app(new_minor, nargs, args));
                minors[i] = erase_lambda_let_types(locals.mk_lambda(new_minor));
            }
        } else {
            for (unsigned i = 0; i < nminors; i++)
                minors[i] = visit(minors[i]);
        }
    }

    /* We keep only the major and minor premises in cases_on applications. */
    expr visit_cases_on(expr const & fn, buffer<expr> & args) {
        name const & rec_name       = const_name(fn);
        name const & I_name         = rec_name.get_prefix();
        unsigned nparams            = *inductive::get_num_params(env(), I_name);
        unsigned nminors            = *inductive::get_num_minor_premises(env(), I_name);
        unsigned nindices           = *inductive::get_num_indices(env(), I_name);
        unsigned arity              = nparams + 1 /* typeformer/motive */ + nindices + 1 /* major premise */ + nminors;
        lean_assert(args.size() >= arity);
        buffer<name> cnames;
        get_intro_rule_names(env(), I_name, cnames);
        expr * minors        = args.data() + nparams + 1 + nindices + 1;
        unsigned nextra_args = args.size() - arity;
        expr * extra_args    = args.data() + arity;
        visit_minors(nparams, nminors, minors, cnames.data(), nextra_args, extra_args);
        expr major  = visit(args[nparams + 1 + nindices]);
        expr new_fn = visit(fn);
        return mk_app(mk_app(new_fn, major), nminors, minors);
    }

    /* We keep only the major and minor premises in rec applications.
       This method also converts the rec into cases_on */
    expr visit_rec(expr const & fn, buffer<expr> & args) {
        name const & rec_name       = const_name(fn);
        name const & I_name         = rec_name.get_prefix();
        /* This preprocessing step assumes that recursive recursors have already been eliminated */
        lean_assert(!is_recursive_datatype(env(), I_name));
        unsigned nparams            = *inductive::get_num_params(env(), I_name);
        unsigned nminors            = *inductive::get_num_minor_premises(env(), I_name);
        unsigned nindices           = *inductive::get_num_indices(env(), I_name);
        unsigned arity              = nparams + 1 /* typeformer/motive */ + nminors + nindices + 1 /* major premise */;
        lean_assert(args.size() >= arity);
        buffer<name> cnames;
        get_intro_rule_names(env(), I_name, cnames);
        expr new_fn = mk_constant(name(I_name, "cases_on"));
        expr major  = visit(args[nparams + 1 + nminors + nindices]);
        expr * minors        = args.data() + nparams + 1;
        unsigned nextra_args = args.size() - arity;
        expr * extra_args    = args.data() + arity;
        visit_minors(nparams, nminors, minors, cnames.data(), nextra_args, extra_args);
        return mk_app(mk_app(new_fn, major), nminors, minors);
    }

    expr add_args(expr e, unsigned start_idx, buffer<expr> const & args) {
        for (unsigned i = start_idx; i < args.size(); i++)
            e = mk_app(e, args[i]);
        return beta_reduce(e);
    }

    /* Remove eq.rec applications since they are just "type-casting" operations. */
    expr visit_eq_rec(buffer<expr> & args) {
        lean_assert(args.size() >= 6);
        expr major = visit(args[3]);
        return add_args(major, 6, args);
    }

    expr consume_lambdas(type_context::tmp_locals & locals, expr e) {
        while (true) {
            if (is_lambda(e)) {
                expr local = locals.push_local_from_binding(e);
                e = instantiate(binding_body(e), local);
            } else {
                return beta_reduce(e);
            }
        }
    }

    /* We can eliminate no_confusion applications, they do not add any relevant information to the environment */
    expr visit_no_confusion(expr const & fn, buffer<expr> const & args) {
        lean_assert(is_constant(fn));
        name const & no_confusion_name  = const_name(fn);
        name const & I_name             = no_confusion_name.get_prefix();
        unsigned nparams                = *inductive::get_num_params(env(), I_name);
        unsigned nindices               = *inductive::get_num_indices(env(), I_name);
        DEBUG_CODE(unsigned basic_arity = nparams + nindices + 1 /* motive */ + 2 /* lhs/rhs */ + 1 /* equality */;);
        lean_assert(args.size() >= basic_arity);
        expr lhs                        = ctx().whnf(args[nparams + nindices + 1]);
        expr rhs                        = ctx().whnf(args[nparams + nindices + 2]);
        optional<name> lhs_constructor  = is_constructor_app(env(), lhs);
        optional<name> rhs_constructor  = is_constructor_app(env(), rhs);
        if (!lhs_constructor || !rhs_constructor)
            throw exception(sstream() << "code generation failed, unsupported occurrence of '" << no_confusion_name << "', constructors expected");
        if (lhs_constructor != rhs_constructor)
            return *g_unreachable_expr;
        lean_assert(args.size() >= basic_arity + 1);
        expr major = args[nparams + nindices + 4];
        type_context::tmp_locals locals(ctx());
        major = consume_lambdas(locals, major);
        major = visit(major);
        major = erase_lambda_let_types(locals.mk_lambda(major));
        expr r = major;

        unsigned c_data_sz = get_constructor_arity(env(), *lhs_constructor) - nparams;
        for (unsigned i = 0; i < c_data_sz; i++)
            r = mk_app(r, *g_neutral_expr); // add dummy proofs
        /* add remaining arguments */
        return add_args(r, nparams + nindices + 5, args);
    }

    /* Treat subtype.tag as the identity function */
    virtual expr visit_subtype_tag(buffer<expr> const & args) {
        lean_assert(args.size() >= 4);
        expr r = visit(args[2]);
        return add_args(r, 4, args);
    }

    /* Eliminate subtype.rec */
    virtual expr visit_subtype_rec(buffer<expr> const & args) {
        lean_assert(args.size() >= 5);
        expr minor = visit(args[3]);
        expr major = visit(args[4]);
        expr r     = mk_app(minor, major, *g_neutral_expr);
        return add_args(r, 5, args);
    }

    /* subtype.elt_of is also compiled as the identity function */
    virtual expr visit_subtype_elt_of(buffer<expr> const & args) {
        lean_assert(args.size() >= 3);
        expr r = visit(args[2]);
        return add_args(r, 3, args);
    }

    virtual expr visit_app(expr const & e) override {
        if (is_comp_irrelevant(ctx(), e))
            return *g_neutral_expr;
        buffer<expr> args;
        expr const & fn = get_app_args(e, args);
        if (is_lambda(fn)) {
            return visit(beta_reduce(e));
        } else if (is_constant(fn)) {
            name const & n = const_name(fn);
            if (n == get_eq_rec_name()) {
                return visit_eq_rec(args);
            } else if (n == get_subtype_rec_name()) {
                return visit_subtype_rec(args);
            } else if (is_cases_on_recursor(env(), n)) {
                return visit_cases_on(fn, args);
            } else if (inductive::is_elim_rule(env(), n)) {
                return visit_rec(fn, args);
            } else if (is_no_confusion(env(), n)) {
                return visit_no_confusion(fn, args);
            } else if (n == get_subtype_tag_name()) {
                return visit_subtype_tag(args);
            } else if (n == get_subtype_elt_of_name()) {
                return visit_subtype_elt_of(args);
            }
        }
        return compiler_step_visitor::visit_app(e);
    }

public:
    erase_irrelevant_fn(environment const & env):compiler_step_visitor(env) {}
};

expr erase_irrelevant(environment const & env, expr const & e) {
    return erase_irrelevant_fn(env)(e);
}

bool is_neutral_expr(expr const & e) {
    return e == *g_neutral_expr;
}

bool is_unreachable_expr(expr const & e) {
    return e == *g_unreachable_expr;
}

void initialize_erase_irrelevant() {
    g_neutral_expr     = new expr(mk_constant("_neutral_"));
    g_unreachable_expr = new expr(mk_constant("_unreachable_"));
}

void finalize_erase_irrelevant() {
    delete g_neutral_expr;
    delete g_unreachable_expr;
}
}
<|endoftext|>


<commit_before>/*********************************************************************
 *
 * Condor ClassAd library
 * Copyright (C) 1990-2001, CONDOR Team, Computer Sciences Department,
 * University of Wisconsin-Madison, WI, and Rajesh Raman.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of version 2.1 of the GNU Lesser General
 * Public License as published by the Free Software Foundation.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
 * USA
 *
 *********************************************************************/

#include "common.h"
#include "indexfile.h"

BEGIN_NAMESPACE( classad );

#ifdef CLASSAD_DISTRIBUTION
string CondorErrMsg;
int CondorErrno;
#endif

int IndexFile::
dump_index()
{
	hash_map<string,int,StringHash>::iterator m=Index.begin();
	cout << "in dump index the lenght= " << Index.size() << endl;
	while (m!=Index.end()){
		cout << "dump index  key= " << m->first << "  offset=" << m->second << endl;
		m++;
	};
	return 1;
}

bool IndexFile::
TruncateStorageFile()
{
	int cur_set;
	int new_filed;
	char* filename="temp_file";
	char* logfilename="storagefile";
	hash_map<string,int,StringHash>::iterator ptr;
	if( ( new_filed = open(filename, O_RDWR | O_CREAT | O_APPEND, 0600 )) < 0 ) {
		CondorErrno = ERR_CACHE_FILE_ERROR;
		CondorErrMsg = "internal error:  unable to create the temp file in truncating storagefile";
		return( false );
	};

	for (ptr=Index.begin();ptr!=Index.end();ptr++){

		lseek(filed,ptr->second,SEEK_SET);
		char  k[1];
		string m;
		int l;

		while ((l=read(filed,k,1))>0){
			string n(k,1);
			if (n=="\n"){
				break;
			} else {
				m=m+n;
			}
		}
		m=m+'\n';
		if (m[0]!='*'){
			if (write(new_filed,(void *)(m.c_str()),m.size())<0){
				return false;
			} else {
				fsync(filed);
				ptr->second=cur_set;
				cur_set+=m.size();
			}
		}
	}

	fsync(new_filed);
	if( rename(filename, logfilename) < 0 ) {
		CondorErrno = ERR_CACHE_FILE_ERROR;
		char buf[10];
		sprintf( buf, "%d", errno );
		CondorErrMsg = "failed to truncate storagefile: rename("
			+ string(filename) + " , "
			+ string(logfilename) +", errno="
			+ string(buf);
		return( false );
	}
	return true;
}

int IndexFile::
First(string &key)
{
	index_itr=Index.begin();
	if (index_itr != Index.end()){
		key=index_itr->first;
		return index_itr->second;
	} else {
		return -1;
	}
}

int IndexFile::
Next(string &key)
{
	index_itr++;
	if (index_itr != Index.end()){
		key=index_itr->first;
		return index_itr->second;
	} else {
		return -1;
	}
}

GetClassadFromFile(string key, int offset)
{
	if (filed != 0){
		int curset;
		curset = lseek(filed,offset,SEEK_SET);
		char  k[1];
		string m;
		int l;

		while ((l=read(filed,k,1))>0){
			string n(k,1);
			if (n=="\n"){
				break;
			} else {
				m=m+n;
			}
		}

		if (m[0] != '*'){
			return m;
		} else {
			return "";
		}
	}else{
	        return "";
	}
}

bool IndexFile::
UpdateIndex(string key, int offset)
{
	Index[key]=offset;
	//cout << Index[key] << endl;
	return true;
}

void IndexFile::
Init(int file_handler)
{
	filed=file_handler;
	Index.clear();
}

bool IndexFile::
WriteBack(string key, string ad)
{
	DeleteFromStorageFile(key);
	int k=lseek(filed,0,SEEK_END);
	Index[key]=k;
	ad=ad+"\n";
	if (write(filed,(void *)(ad.c_str()),ad.size())<0){
		return false;
	} else {
		fsync(filed);
		return true;
	};

}

bool IndexFile::
FindInFile(string key,tag &ptr)
{
	hash_map<string,int,StringHash>::iterator m=Index.find(key);
	if (m!=Index.end()){
		ptr.offset=m->second;
		return true;
	} else {
		return false;
	}
}

bool IndexFile::
DeleteFromStorageFile(string key)
{
	hash_map<string,int,StringHash>::iterator m=Index.find(key);
	if (m!=Index.end()){
		int offset=m->second;
		lseek(filed,offset,SEEK_SET);
		char  k[1];
		string m;
		int l;

		while ((l=read(filed,k,1))>0){
			string n(k,1);
			if (n=="\n"){
				break;
			} else {
				m=m+n;
			}
		}

		m[0]='*';
		m=m+'\n';
		lseek(filed,offset,SEEK_SET);
		write(filed,(void *)(m.c_str()),m.size());
		fsync(filed);
		Index.erase(key);
		return true;
	} else {
		return false;
	}
}

END_NAMESPACE

<commit_msg>*** empty log message ***<commit_after>/*********************************************************************
 *
 * Condor ClassAd library
 * Copyright (C) 1990-2001, CONDOR Team, Computer Sciences Department,
 * University of Wisconsin-Madison, WI, and Rajesh Raman.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of version 2.1 of the GNU Lesser General
 * Public License as published by the Free Software Foundation.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
 * USA
 *
 *********************************************************************/

#include "common.h"
#include "indexfile.h"

BEGIN_NAMESPACE( classad );

#ifdef CLASSAD_DISTRIBUTION
string CondorErrMsg;
int CondorErrno;
#endif

int IndexFile::
dump_index()
{
   	hash_map<string,int,StringHash>::iterator m=Index.begin();
	cout << "in dump index the lenght= " << Index.size() << endl;
	while (m!=Index.end()){
		cout << "dump index  key= " << m->first << "  offset=" << m->second << endl;
		m++;
	};
	return 1;
}

bool IndexFile::
TruncateStorageFile()
{
	int cur_set;
	int new_filed;
	char* filename="temp_file";
	char* logfilename="storagefile";
	hash_map<string,int,StringHash>::iterator ptr;
	if( ( new_filed = open(filename, O_RDWR | O_CREAT | O_APPEND, 0600 )) < 0 ) {
		CondorErrno = ERR_CACHE_FILE_ERROR;
		CondorErrMsg = "internal error:  unable to create the temp file in truncating storagefile";
		return( false );
	};

	for (ptr=Index.begin();ptr!=Index.end();ptr++){

		lseek(filed,ptr->second,SEEK_SET);
		char  k[1];
		string m;
		int l;

		while ((l=read(filed,k,1))>0){
			string n(k,1);
			if (n=="\n"){
				break;
			} else {
				m=m+n;
			}
		}
		m=m+'\n';
		if (m[0]!='*'){
			if (write(new_filed,(void *)(m.c_str()),m.size())<0){
				return false;
			} else {
				fsync(filed);
				ptr->second=cur_set;
				cur_set+=m.size();
			}
		}
	}

	fsync(new_filed);
	if( rename(filename, logfilename) < 0 ) {
		CondorErrno = ERR_CACHE_FILE_ERROR;
		char buf[10];
		sprintf( buf, "%d", errno );
		CondorErrMsg = "failed to truncate storagefile: rename("
			+ string(filename) + " , "
			+ string(logfilename) +", errno="
			+ string(buf);
		return( false );
	}
	return true;
}

int IndexFile::
First(string &key)
{
	index_itr=Index.begin();
	if (index_itr != Index.end()){
		key=index_itr->first;
		return index_itr->second;
	} else {
		return -1;
	}
}

int IndexFile::
Next(string &key)
{
	index_itr++;
	if (index_itr != Index.end()){
		key=index_itr->first;
		return index_itr->second;
	} else {
		return -1;
	}
}

string IndexFile::
GetClassadFromFile(string key, int offset)
{
	if (filed != 0){
		int curset;
		curset = lseek(filed,offset,SEEK_SET);
		char  k[1];
		string m;
		int l;

		while ((l=read(filed,k,1))>0){
			string n(k,1);
			if (n=="\n"){
				break;
			} else {
				m=m+n;
			}
		}

		if (m[0] != '*'){
			return m;
		} else {
			return "";
		}
	}else{
	        return "";
	}
}

bool IndexFile::
UpdateIndex(string key, int offset)
{
	Index[key]=offset;
	//cout << Index[key] << endl;
	return true;
}

void IndexFile::
Init(int file_handler)
{
	filed=file_handler;
	Index.clear();
}

bool IndexFile::
WriteBack(string key, string ad)
{
	DeleteFromStorageFile(key);
	int k=lseek(filed,0,SEEK_END);
	Index[key]=k;
	ad=ad+"\n";
	if (write(filed,(void *)(ad.c_str()),ad.size())<0){
		return false;
	} else {
		fsync(filed);
		return true;
	};

}

bool IndexFile::
FindInFile(string key,tag &ptr)
{
	hash_map<string,int,StringHash>::iterator m=Index.find(key);
	if (m!=Index.end()){
		ptr.offset=m->second;
		return true;
	} else {
		return false;
	}
}

bool IndexFile::
DeleteFromStorageFile(string key)
{
	hash_map<string,int,StringHash>::iterator m=Index.find(key);
	if (m!=Index.end()){
		int offset=m->second;
		lseek(filed,offset,SEEK_SET);
		char  k[1];
		string m;
		int l;

		while ((l=read(filed,k,1))>0){
			string n(k,1);
			if (n=="\n"){
				break;
			} else {
				m=m+n;
			}
		}

		m[0]='*';
		m=m+'\n';
		lseek(filed,offset,SEEK_SET);
		write(filed,(void *)(m.c_str()),m.size());
		fsync(filed);
		Index.erase(key);
		return true;
	} else {
		return false;
	}
}

END_NAMESPACE

<|endoftext|>


<commit_before>#include "config_parser.hh"

// #define BOOST_SPIRIT_DEBUG 1

#define BOOST_SPIRIT_USE_PHOENIX_V3 1

#include <boost/fusion/include/std_pair.hpp>
#include <boost/fusion/include/adapt_adt.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix_function.hpp>
#include <boost/spirit/include/phoenix_object.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/support_line_pos_iterator.hpp>
#include <boost/variant.hpp>
#include <libnova/julian_day.h>
#include <libnova/ln_types.h>

#include "exceptions.hh"
#include "now.hh"

typedef std::pair<std::string, std::string> pair_type;
typedef boost::variant<std::string, pair_type> config_variant;

#ifdef BOOST_SPIRIT_DEBUG
    namespace std
    {
    std::ostream & operator<<(std::ostream & os, const pair_type & p)
    {
        os << p.first << ": " << p.second;
        return os;
    }
    }
#endif

namespace config_parser
{

namespace phoenix = boost::phoenix;
namespace spirit = boost::spirit;
namespace ascii = spirit::ascii;
namespace qi = spirit::qi;

using ascii::alpha;
using ascii::alnum;
using ascii::blank;
using ascii::char_;
using ascii::graph;
using ascii::space;
using spirit::_1;
using spirit::_2;
using spirit::_3;
using spirit::_4;
using spirit::_val;
using spirit::double_;
using spirit::eoi;
using spirit::eol;
using spirit::lexeme;
using spirit::lit;
using spirit::omit;
using spirit::skip;

struct parser_proxy
    : boost::static_visitor<>
{
    typedef config_variant value_type;

    SectionCallback scal_;
    ValueCallback vcal_;

    std::string current_name;

    parser_proxy(const SectionCallback & scal, const ValueCallback & vcal)
        : scal_(scal),
          vcal_(vcal),
          current_name("core")
    {
    }

    void push_back(const config_variant & v)
    {
        boost::apply_visitor(*this, v);
    }

    void operator()(const std::string & section)
    {
        current_name = section;
        scal_(section);
    }

    void operator()(const pair_type & p)
    {
        vcal_(current_name + '.' + p.first, p.second);
    }

    config_variant & get() const
    {
        static config_variant p;
        return p;
    }
};

template <typename Iterator, typename Skipper>
class config_grammar
    : public qi::grammar<Iterator, parser_proxy(), Skipper>
{
    struct error_handler_imp
    {
        typedef void result_type;

        Iterator begin;

        explicit error_handler_imp(Iterator b)
            : begin(b)
        {
        }

        void operator()(Iterator first, Iterator last,
                        Iterator err_pos, const boost::spirit::info &) const
        {
            std::ostringstream os;
            os << "Parsing failed at line " << spirit::get_line(err_pos)
               << ", near: " << spirit::get_current_line(begin, first, last);
            throw ConfigError(os.str());
        }
    };

public:
    config_grammar(Iterator begin)
        : config_grammar::base_type(config),
          error_handler(error_handler_imp(begin))

    {
        identifier =
            alpha[_val += _1] >> *(char_('.') | alnum)[_val += _1];

        value %=
            lexeme['"' > *~ascii::char_('"') > '"'] |
            +(graph - '"');

        entry_rule =
            identifier > value > (eol | eoi);

        section =
            "[" > identifier > "]";

        config %=
            *(section | skip(blank)[entry_rule]);

        qi::on_error<qi::fail>(value, error_handler(_1, _2, _3, _4));

#ifdef BOOST_SPIRIT_DEBUG
        BOOST_SPIRIT_DEBUG_NODE(identifier);
        BOOST_SPIRIT_DEBUG_NODE(value);
        BOOST_SPIRIT_DEBUG_NODE(entry_rule);
        BOOST_SPIRIT_DEBUG_NODE(section);
        BOOST_SPIRIT_DEBUG_NODE(config);
#endif
    }

    qi::rule<Iterator, parser_proxy(), Skipper> config;
    qi::rule<Iterator, std::string()> identifier, value, section;
    qi::rule<Iterator, pair_type(), ascii::blank_type> entry_rule;

    phoenix::function<error_handler_imp> error_handler;
};

void parse_config(std::istream & is, const SectionCallback & scal, const ValueCallback & vcal)
{
    std::stringstream ss;
    ss << is.rdbuf();
    std::string s(ss.str());
    spirit::line_pos_iterator<decltype(s.cbegin())> begin(s.cbegin()), iter(begin), iter_end(s.cend());
    typedef decltype(iter) Iter;

    auto skipper(ascii::space | '#' >> *(qi::char_ - qi::eol) >> qi::eol);
    config_grammar<Iter, decltype(skipper)> pars(begin);
    parser_proxy t(scal, vcal);
    try
    {
        bool r(qi::phrase_parse(iter, iter_end, pars, skipper, t));
        if (! r or iter != iter_end)
        {
            throw ConfigError("parser failed");
        }
    }
    catch (const spirit::qi::expectation_failure<Iter> & x)
    {
        auto range(spirit::get_current_line(begin, x.first, iter_end));
        std::ostringstream os;
        os << "Parsing failed at line: " << x.first.position() << ": " << range;
        throw ConfigError(os.str());
    }
}

const double rad2deg(45. / atan(1.));

template <typename Iterator>
class angle_grammar
    : public qi::grammar<Iterator, double()>
{
public:
    angle_grammar()
        : angle_grammar::base_type(start)
    {
        radians = double_[_val = rad2deg * _1] >> 'r';
        degrees = double_[_val = _1] >> -('d' >>
            -(double_[_val += _1 / 60.] >> 'm' >>
              -(double_[_val += _1/ 3600.] >> 's')));
        start = degrees | radians;
    }

    qi::rule<Iterator, double()> start;
    qi::rule<Iterator, double()> radians;
    qi::rule<Iterator, double()> degrees;
};

double parse_angle(const std::string & in)
{
    angle_grammar<std::string::const_iterator> pars;
    double ret;
    auto begin(in.cbegin()), end(in.cend());
    bool r(parse(begin, end, pars, ret));
    if (!r or begin != end)
    {
        throw ConfigValueError("angle", in);
    }
    return ret;
}

struct julianepoch_to_juliandate_imp
{
    typedef double result_type;

    double operator()(double epoch) const
    {
        return (epoch - 2000.0) * 365.25 + 2451545.0;
    }
};
phoenix::function<julianepoch_to_juliandate_imp> julianepoch_to_juliandate;

struct parseddate_to_juliandate_imp
{
    typedef double result_type;

    double operator()(int h, int m, boost::optional<double> s,
                      boost::optional<boost::fusion::vector3<int, int, int>> date,
                      boost::optional<int> tz) const
    {
        ln_zonedate zdate;

        if (date)
        {
            using boost::fusion::at_c;
            zdate.days = at_c<2>(*date);
            zdate.months = at_c<1>(*date);
            zdate.years = at_c<0>(*date);
        }
        else
        {
            ln_date now(Now::get());
            zdate.days = now.days;
            zdate.months = now.months;
            zdate.years = now.years;
        }

        if (! s)
            s = 0;

        zdate.seconds = *s;
        zdate.minutes = m;
        zdate.hours = h;

        if (tz)
            zdate.gmtoff = *tz * 3600;
        else
            zdate.gmtoff = 0;

        return ln_get_julian_local_date(&zdate);
    }
};
phoenix::function<parseddate_to_juliandate_imp> parseddate_to_juliandate;

struct now_get_imp
{
    typedef double result_type;

    double operator()() const
    {
        auto d(Now::get());
        return ln_get_julian_day(&d);
    }
};
phoenix::function<now_get_imp> now_get;

template <typename Iterator>
class timestamp_grammar
        : public qi::grammar<Iterator, double()>
{
public:
    timestamp_grammar()
        : timestamp_grammar::base_type(start)
    {
        using namespace boost::spirit::qi;
        start = (double_ >> "JD") [_val = _1]
            | ("J" > double_) [_val = julianepoch_to_juliandate(_1)]
            | (
                int_ >> ":" >> int_ >> -(":" >> double_) >>
                -(omit[space] >> int_ >> "-" >> int_ >> "-" >> int_) >>
                -(omit[space] >> int_)
                ) [_val = parseddate_to_juliandate(_1, _2, _3, _4, _5)]
            | lit("now") [_val = now_get()]
            ;
    }

    qi::rule<Iterator, double()> start;
};

double parse_timestamp(const std::string & in)
{
    timestamp_grammar<std::string::const_iterator> pars;
    double ret;
    auto begin(in.cbegin()), end(in.cend());
    bool r(parse(begin, end, pars, ret));
    if (!r or begin != end)
    {
        throw ConfigValueError("timestamp", in);
    }
    return ret;
}

template <typename Iterator>
class length_grammar
        : public qi::grammar<Iterator, double()>
{
public:
    length_grammar()
        : length_grammar::base_type(start)
    {
        using namespace boost::spirit::qi;
        start %= (double_ >> -lit("mm"));
    }

    qi::rule<Iterator, double()> start;
};

double parse_length(const std::string & in)
{
    length_grammar<std::string::const_iterator> pars;
    double ret;
    auto begin(in.cbegin()), end(in.cend());
    bool r(parse(begin, end, pars, ret));
    if (!r or begin != end)
    {
        throw ConfigValueError("length", in);
    }
    return ret;
}

}

BOOST_FUSION_ADAPT_ADT(
    config_parser::parser_proxy,
    (const config_variant &, const config_variant &, obj.get(), obj.push_back(val))
)

namespace boost { namespace spirit { namespace traits
{

template <>
struct push_back_container<config_parser::parser_proxy, config_variant>
{
    static bool call(config_parser::parser_proxy & t, const config_variant & val)
    {
        t.push_back(val);
        return true;
    }
};

#ifdef BOOST_SPIRIT_DEBUG
    // SPIRIT_DEBUG doesn't work otherwise

    std::ostream & operator<<(std::ostream & os, const config_variant & v)
    {
        using boost::operator<<;
        os << v;
        return os;
    }

#endif

}}}
<commit_msg>allow fractional timezones<commit_after>#include "config_parser.hh"

// #define BOOST_SPIRIT_DEBUG 1

#define BOOST_SPIRIT_USE_PHOENIX_V3 1

#include <boost/fusion/include/std_pair.hpp>
#include <boost/fusion/include/adapt_adt.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix_function.hpp>
#include <boost/spirit/include/phoenix_object.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/support_line_pos_iterator.hpp>
#include <boost/variant.hpp>
#include <libnova/julian_day.h>
#include <libnova/ln_types.h>

#include "exceptions.hh"
#include "now.hh"

typedef std::pair<std::string, std::string> pair_type;
typedef boost::variant<std::string, pair_type> config_variant;

#ifdef BOOST_SPIRIT_DEBUG
    namespace std
    {
    std::ostream & operator<<(std::ostream & os, const pair_type & p)
    {
        os << p.first << ": " << p.second;
        return os;
    }
    }
#endif

namespace config_parser
{

namespace phoenix = boost::phoenix;
namespace spirit = boost::spirit;
namespace ascii = spirit::ascii;
namespace qi = spirit::qi;

using ascii::alpha;
using ascii::alnum;
using ascii::blank;
using ascii::char_;
using ascii::graph;
using ascii::space;
using spirit::_1;
using spirit::_2;
using spirit::_3;
using spirit::_4;
using spirit::_val;
using spirit::double_;
using spirit::eoi;
using spirit::eol;
using spirit::lexeme;
using spirit::lit;
using spirit::omit;
using spirit::skip;

struct parser_proxy
    : boost::static_visitor<>
{
    typedef config_variant value_type;

    SectionCallback scal_;
    ValueCallback vcal_;

    std::string current_name;

    parser_proxy(const SectionCallback & scal, const ValueCallback & vcal)
        : scal_(scal),
          vcal_(vcal),
          current_name("core")
    {
    }

    void push_back(const config_variant & v)
    {
        boost::apply_visitor(*this, v);
    }

    void operator()(const std::string & section)
    {
        current_name = section;
        scal_(section);
    }

    void operator()(const pair_type & p)
    {
        vcal_(current_name + '.' + p.first, p.second);
    }

    config_variant & get() const
    {
        static config_variant p;
        return p;
    }
};

template <typename Iterator, typename Skipper>
class config_grammar
    : public qi::grammar<Iterator, parser_proxy(), Skipper>
{
    struct error_handler_imp
    {
        typedef void result_type;

        Iterator begin;

        explicit error_handler_imp(Iterator b)
            : begin(b)
        {
        }

        void operator()(Iterator first, Iterator last,
                        Iterator err_pos, const boost::spirit::info &) const
        {
            std::ostringstream os;
            os << "Parsing failed at line " << spirit::get_line(err_pos)
               << ", near: " << spirit::get_current_line(begin, first, last);
            throw ConfigError(os.str());
        }
    };

public:
    config_grammar(Iterator begin)
        : config_grammar::base_type(config),
          error_handler(error_handler_imp(begin))

    {
        identifier =
            alpha[_val += _1] >> *(char_('.') | alnum)[_val += _1];

        value %=
            lexeme['"' > *~ascii::char_('"') > '"'] |
            +(graph - '"');

        entry_rule =
            identifier > value > (eol | eoi);

        section =
            "[" > identifier > "]";

        config %=
            *(section | skip(blank)[entry_rule]);

        qi::on_error<qi::fail>(value, error_handler(_1, _2, _3, _4));

#ifdef BOOST_SPIRIT_DEBUG
        BOOST_SPIRIT_DEBUG_NODE(identifier);
        BOOST_SPIRIT_DEBUG_NODE(value);
        BOOST_SPIRIT_DEBUG_NODE(entry_rule);
        BOOST_SPIRIT_DEBUG_NODE(section);
        BOOST_SPIRIT_DEBUG_NODE(config);
#endif
    }

    qi::rule<Iterator, parser_proxy(), Skipper> config;
    qi::rule<Iterator, std::string()> identifier, value, section;
    qi::rule<Iterator, pair_type(), ascii::blank_type> entry_rule;

    phoenix::function<error_handler_imp> error_handler;
};

void parse_config(std::istream & is, const SectionCallback & scal, const ValueCallback & vcal)
{
    std::stringstream ss;
    ss << is.rdbuf();
    std::string s(ss.str());
    spirit::line_pos_iterator<decltype(s.cbegin())> begin(s.cbegin()), iter(begin), iter_end(s.cend());
    typedef decltype(iter) Iter;

    auto skipper(ascii::space | '#' >> *(qi::char_ - qi::eol) >> qi::eol);
    config_grammar<Iter, decltype(skipper)> pars(begin);
    parser_proxy t(scal, vcal);
    try
    {
        bool r(qi::phrase_parse(iter, iter_end, pars, skipper, t));
        if (! r or iter != iter_end)
        {
            throw ConfigError("parser failed");
        }
    }
    catch (const spirit::qi::expectation_failure<Iter> & x)
    {
        auto range(spirit::get_current_line(begin, x.first, iter_end));
        std::ostringstream os;
        os << "Parsing failed at line: " << x.first.position() << ": " << range;
        throw ConfigError(os.str());
    }
}

const double rad2deg(45. / atan(1.));

template <typename Iterator>
class angle_grammar
    : public qi::grammar<Iterator, double()>
{
public:
    angle_grammar()
        : angle_grammar::base_type(start)
    {
        radians = double_[_val = rad2deg * _1] >> 'r';
        degrees = double_[_val = _1] >> -('d' >>
            -(double_[_val += _1 / 60.] >> 'm' >>
              -(double_[_val += _1/ 3600.] >> 's')));
        start = degrees | radians;
    }

    qi::rule<Iterator, double()> start;
    qi::rule<Iterator, double()> radians;
    qi::rule<Iterator, double()> degrees;
};

double parse_angle(const std::string & in)
{
    angle_grammar<std::string::const_iterator> pars;
    double ret;
    auto begin(in.cbegin()), end(in.cend());
    bool r(parse(begin, end, pars, ret));
    if (!r or begin != end)
    {
        throw ConfigValueError("angle", in);
    }
    return ret;
}

struct julianepoch_to_juliandate_imp
{
    typedef double result_type;

    double operator()(double epoch) const
    {
        return (epoch - 2000.0) * 365.25 + 2451545.0;
    }
};
phoenix::function<julianepoch_to_juliandate_imp> julianepoch_to_juliandate;

struct parseddate_to_juliandate_imp
{
    typedef double result_type;

    double operator()(int h, int m, boost::optional<double> s,
                      boost::optional<boost::fusion::vector3<int, int, int>> date,
                      boost::optional<double> tz) const
    {
        ln_zonedate zdate;

        if (date)
        {
            using boost::fusion::at_c;
            zdate.days = at_c<2>(*date);
            zdate.months = at_c<1>(*date);
            zdate.years = at_c<0>(*date);
        }
        else
        {
            ln_date now(Now::get());
            zdate.days = now.days;
            zdate.months = now.months;
            zdate.years = now.years;
        }

        if (! s)
            s = 0;

        zdate.seconds = *s;
        zdate.minutes = m;
        zdate.hours = h;

        if (tz)
            zdate.gmtoff = *tz * 3600;
        else
            zdate.gmtoff = 0;

        return ln_get_julian_local_date(&zdate);
    }
};
phoenix::function<parseddate_to_juliandate_imp> parseddate_to_juliandate;

struct now_get_imp
{
    typedef double result_type;

    double operator()() const
    {
        auto d(Now::get());
        return ln_get_julian_day(&d);
    }
};
phoenix::function<now_get_imp> now_get;

template <typename Iterator>
class timestamp_grammar
        : public qi::grammar<Iterator, double()>
{
public:
    timestamp_grammar()
        : timestamp_grammar::base_type(start)
    {
        using namespace boost::spirit::qi;
        start = (double_ >> "JD") [_val = _1]
            | ("J" > double_) [_val = julianepoch_to_juliandate(_1)]
            | (
                int_ >> ":" >> int_ >> -(":" >> double_) >>
                -(omit[space] >> int_ >> "-" >> int_ >> "-" >> int_) >>
                -(omit[space] >> double_)
                ) [_val = parseddate_to_juliandate(_1, _2, _3, _4, _5)]
            | lit("now") [_val = now_get()]
            ;
    }

    qi::rule<Iterator, double()> start;
};

double parse_timestamp(const std::string & in)
{
    timestamp_grammar<std::string::const_iterator> pars;
    double ret;
    auto begin(in.cbegin()), end(in.cend());
    bool r(parse(begin, end, pars, ret));
    if (!r or begin != end)
    {
        throw ConfigValueError("timestamp", in);
    }
    return ret;
}

template <typename Iterator>
class length_grammar
        : public qi::grammar<Iterator, double()>
{
public:
    length_grammar()
        : length_grammar::base_type(start)
    {
        using namespace boost::spirit::qi;
        start %= (double_ >> -lit("mm"));
    }

    qi::rule<Iterator, double()> start;
};

double parse_length(const std::string & in)
{
    length_grammar<std::string::const_iterator> pars;
    double ret;
    auto begin(in.cbegin()), end(in.cend());
    bool r(parse(begin, end, pars, ret));
    if (!r or begin != end)
    {
        throw ConfigValueError("length", in);
    }
    return ret;
}

}

BOOST_FUSION_ADAPT_ADT(
    config_parser::parser_proxy,
    (const config_variant &, const config_variant &, obj.get(), obj.push_back(val))
)

namespace boost { namespace spirit { namespace traits
{

template <>
struct push_back_container<config_parser::parser_proxy, config_variant>
{
    static bool call(config_parser::parser_proxy & t, const config_variant & val)
    {
        t.push_back(val);
        return true;
    }
};

#ifdef BOOST_SPIRIT_DEBUG
    // SPIRIT_DEBUG doesn't work otherwise

    std::ostream & operator<<(std::ostream & os, const config_variant & v)
    {
        using boost::operator<<;
        os << v;
        return os;
    }

#endif

}}}
<|endoftext|>


<commit_before>/*******************************************************************************
 * Copyright (c) 2014 Varuna L Amachi. All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 ******************************************************************************/
#include <QEvent>
#include <QCoreApplication>

#include "JobManager.h"

namespace Tanyatu {

namespace {
class StdJob : public IJob
{
public:
    StdJob( QString name,
            JobFunc job,
            bool guiDispatch,
            QString category )
        : m_name( name )
        , m_jobFunc( job )
        , m_isGuiDispatch( guiDispatch )
        , m_category( category )
    {

    }

    const QString & name() const
    {
        return m_name;
    }

    const QString & category() const
    {
        return m_category;
    }

    bool shouldPostResponse() const
    {
        return m_isGuiDispatch;
    }

    RespFunc execute()
    {
        return m_jobFunc();
    }

private:
    QString m_name;

    JobFunc m_jobFunc;

    bool m_isGuiDispatch;

    QString m_category;
};

class CallbackEvent : public QEvent
{
public:
    CallbackEvent( RespFunc callback )
        : QEvent( CallbackEvent::type() )
        , m_callback( callback )
    {

    }

    void invokeCallback()
    {
        if( m_callback ) m_callback();
    }

    static QEvent::Type type()
    {
        QEvent::registerEventType( CallbackEvent::EVENT_TYPE );
        return CallbackEvent::EVENT_TYPE;
    }

    static const QEvent::Type EVENT_TYPE;

private:
    RespFunc m_callback;
};

} //end of anonymous namespace

const QEvent::Type CallbackEvent::EVENT_TYPE
                                          = static_cast< QEvent::Type >( 2334 );

JobManager::JobManager( QObject *parent )
    : QThread( parent )
{

}

bool JobManager::hasPendingJobs()
{
    SCOPE_LIMIT( m_lock.lockForRead(), m_lock.unlock() );
    return ! m_jobs.empty();
}

void JobManager::addJob( IJob *job )
{
    SCOPE_LIMIT( m_lock.lockForWrite(), m_lock.unlock() );
    m_jobs.append( job );
    if( ! this->isRunning() ) {
        this->start();
    }
}

void JobManager::addJob( QString name,
                         JobFunc job,
                         QString category,
                         bool postResponseToEventQueue )
{
    SCOPE_LIMIT( m_lock.lockForWrite(), m_lock.unlock() );
    m_jobs.append( new StdJob( name,
                               job,
                               postResponseToEventQueue,
                               category ));
    if( ! this->isRunning() ) {
        this->start();
    }
}

bool JobManager::cancelJob( QString name )
{
    SCOPE_LIMIT( m_lock.lockForWrite(), m_lock.unlock() );
    return m_cancelledJobs.insert( name ) != m_cancelledJobs.end();
}

bool JobManager::cancelJobCategory( QString category )
{
    SCOPE_LIMIT( m_lock.lockForWrite(), m_lock.unlock() );
    return m_cancelledCategories.insert( category )
                                                != m_cancelledCategories.end();
}

void JobManager::cancelAll()
{
    m_lock.lockForWrite();
    m_stopProcessing = true;
    m_lock.unlock();
    this->wait();
    SCOPE_LIMIT( m_lock.lockForWrite(), m_lock.unlock() );
    foreach( IJob *job, m_jobs ) {
        delete job;
    }
    m_cancelledCategories.clear();
    m_cancelledJobs.clear();
}

void JobManager::run()
{
    bool shouldExit = false;
    bool hasPendingTasks = true;
    IJob *job = nullptr;

    while( ! shouldExit ){
        m_lock.lockForRead();
        shouldExit = m_stopProcessing;
        hasPendingTasks = m_jobs.size() != 0;
        m_lock.unlock();
        if( hasPendingTasks ) {
            m_lock.lockForWrite();
            job = m_jobs.last();
            m_jobs.removeLast();
            bool isCancelled = m_cancelledJobs.remove( job->name() );
            m_lock.unlock();

            if( ! isCancelled ) {
                emit executionStarted( job->name(), job->category() );
                RespFunc func = job->execute();
                emit executionFinished( job->name(), job->category() );
                if( func ) {
                    if( job->shouldPostResponse() ) {
                        QCoreApplication::postEvent(
                                    this,
                                    new CallbackEvent( func ),
                                    100 );
                    }
                    else {
                        func();
                    }
                }
            }
            else {
                emit jobDescarded( job->name(), job->category() );
            }
            delete job;
        }
        else {
            this->sleep( 500 );
        }
    }

}

bool JobManager::event( QEvent *event )
{
    if( event->type() == CallbackEvent::EVENT_TYPE ) {
        CallbackEvent *callbackEvt = static_cast< CallbackEvent *>( event );
        callbackEvt->invokeCallback();
    }
    return QThread::event( event );
}

} //end of namespace
<commit_msg>Reduced the sleep time when there are no jobs, to 1 sec<commit_after>/*******************************************************************************
 * Copyright (c) 2014 Varuna L Amachi. All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 ******************************************************************************/
#include <QEvent>
#include <QCoreApplication>

#include "JobManager.h"

namespace Tanyatu {

namespace {
class StdJob : public IJob
{
public:
    StdJob( QString name,
            JobFunc job,
            bool guiDispatch,
            QString category )
        : m_name( name )
        , m_jobFunc( job )
        , m_isGuiDispatch( guiDispatch )
        , m_category( category )
    {

    }

    const QString & name() const
    {
        return m_name;
    }

    const QString & category() const
    {
        return m_category;
    }

    bool shouldPostResponse() const
    {
        return m_isGuiDispatch;
    }

    RespFunc execute()
    {
        return m_jobFunc();
    }

private:
    QString m_name;

    JobFunc m_jobFunc;

    bool m_isGuiDispatch;

    QString m_category;
};

class CallbackEvent : public QEvent
{
public:
    CallbackEvent( RespFunc callback )
        : QEvent( CallbackEvent::type() )
        , m_callback( callback )
    {

    }

    void invokeCallback()
    {
        if( m_callback ) m_callback();
    }

    static QEvent::Type type()
    {
        QEvent::registerEventType( CallbackEvent::EVENT_TYPE );
        return CallbackEvent::EVENT_TYPE;
    }

    static const QEvent::Type EVENT_TYPE;

private:
    RespFunc m_callback;
};

} //end of anonymous namespace

const QEvent::Type CallbackEvent::EVENT_TYPE
                                          = static_cast< QEvent::Type >( 2334 );

JobManager::JobManager( QObject *parent )
    : QThread( parent )
{

}

bool JobManager::hasPendingJobs()
{
    SCOPE_LIMIT( m_lock.lockForRead(), m_lock.unlock() );
    return ! m_jobs.empty();
}

void JobManager::addJob( IJob *job )
{
    SCOPE_LIMIT( m_lock.lockForWrite(), m_lock.unlock() );
    m_jobs.append( job );
    if( ! this->isRunning() ) {
        this->start();
    }
}

void JobManager::addJob( QString name,
                         JobFunc job,
                         QString category,
                         bool postResponseToEventQueue )
{
    SCOPE_LIMIT( m_lock.lockForWrite(), m_lock.unlock() );
    m_jobs.append( new StdJob( name,
                               job,
                               postResponseToEventQueue,
                               category ));
    if( ! this->isRunning() ) {
        this->start();
    }
}

bool JobManager::cancelJob( QString name )
{
    SCOPE_LIMIT( m_lock.lockForWrite(), m_lock.unlock() );
    return m_cancelledJobs.insert( name ) != m_cancelledJobs.end();
}

bool JobManager::cancelJobCategory( QString category )
{
    SCOPE_LIMIT( m_lock.lockForWrite(), m_lock.unlock() );
    return m_cancelledCategories.insert( category )
                                                != m_cancelledCategories.end();
}

void JobManager::cancelAll()
{
    m_lock.lockForWrite();
    m_stopProcessing = true;
    m_lock.unlock();
    this->wait();
    SCOPE_LIMIT( m_lock.lockForWrite(), m_lock.unlock() );
    foreach( IJob *job, m_jobs ) {
        delete job;
    }
    m_cancelledCategories.clear();
    m_cancelledJobs.clear();
}

void JobManager::run()
{
    bool shouldExit = false;
    bool hasPendingTasks = true;
    IJob *job = nullptr;

    while( ! shouldExit ){
        m_lock.lockForRead();
        shouldExit = m_stopProcessing;
        hasPendingTasks = m_jobs.size() != 0;
        m_lock.unlock();
        if( hasPendingTasks ) {
            m_lock.lockForWrite();
            job = m_jobs.last();
            m_jobs.removeLast();
            bool isCancelled = m_cancelledJobs.remove( job->name() );
            m_lock.unlock();

            if( ! isCancelled ) {
                emit executionStarted( job->name(), job->category() );
                RespFunc func = job->execute();
                emit executionFinished( job->name(), job->category() );
                if( func ) {
                    if( job->shouldPostResponse() ) {
                        QCoreApplication::postEvent(
                                    this,
                                    new CallbackEvent( func ),
                                    100 );
                    }
                    else {
                        func();
                    }
                }
            }
            else {
                emit jobDescarded( job->name(), job->category() );
            }
            delete job;
        }
        else {
            this->sleep( 1 );
        }
    }

}

bool JobManager::event( QEvent *event )
{
    if( event->type() == CallbackEvent::EVENT_TYPE ) {
        CallbackEvent *callbackEvt = static_cast< CallbackEvent *>( event );
        callbackEvt->invokeCallback();
    }
    return QThread::event( event );
}

} //end of namespace
<|endoftext|>


<commit_before>#include "SleepyThread.h"

namespace foo_mpdsrv
{
	SleepyThread::SleepyThread()
	{
		_abort = false;
		_event.create(false, false);
	}
	SleepyThread::SleepyThread(SleepyThread&& right)
	{
		_abort = right._abort;
		_event.set(right._event.get());
		right._event.set(NULL);
	}
	SleepyThread::~SleepyThread()
	{
		_event.set_state(true);
	}

	void SleepyThread::ExitThread()
	{
		_abort = true;
		Wake();
	}

	void SleepyThread::Wake()
	{
		_event.set_state(true);
	}

	unsigned int SleepyThread::ThreadProc(abort_callback& p_abort)
	{
		bool run = true;
		while(run)
		{
			run = _event.wait_for(INFINITE);
			p_abort.check();

			run = run && !_abort;
			if(run)
			{
				Logger log(Logger::FINEST);
				log.Log("Thread received some work.");
				run = WakeProc(p_abort);
			}
		}
		p_abort.check();
		Logger log(Logger::DBG);
		log.Log("Event ");
		log.Log(_event.get());
		log.Log(" canceled.");
		return 0;
	}
}<commit_msg>SleepyThread exiting properly<commit_after>#include "SleepyThread.h"

namespace foo_mpdsrv
{
	SleepyThread::SleepyThread()
	{
		_abort = false;
		_event.create(false, false);
	}
	SleepyThread::SleepyThread(SleepyThread&& right)
	{
		_abort = right._abort;
		_event.set(right._event.get());
		right._event.set(NULL);
	}
	SleepyThread::~SleepyThread()
	{
		ExitThread();
	}

	void SleepyThread::ExitThread()
	{
		_abort = true;
		Wake();
	}

	void SleepyThread::Wake()
	{
		_event.set_state(true);
	}

	unsigned int SleepyThread::ThreadProc(abort_callback& p_abort)
	{
		bool run = true;
		while(run)
		{
			run = _event.wait_for(INFINITE);
			p_abort.check();

			run = run && !_abort;
			if(run)
			{
				Logger log(Logger::FINEST);
				log.Log("Thread received some work.");
				run = WakeProc(p_abort);
			}
		}
		p_abort.check();
		Logger log(Logger::DBG);
		log.Log("Event ");
		log.Log(_event.get());
		log.Log(" canceled.");
		return 0;
	}
}<|endoftext|>