<commit_before>#ifdef STAND_ALONE
#   define BOOST_TEST_MODULE Main
#else
#ifndef _WIN32
#   define BOOST_TEST_MODULE Lexer
#endif
#endif

#include <boost/test/unit_test.hpp>

#include "lexer.h"

using namespace TosLang::FrontEnd;

BOOST_AUTO_TEST_CASE( InitTest )
{
    Lexer lex;
    BOOST_REQUIRE(!lex.Init("BadFile.tos"));
    BOOST_REQUIRE(lex.Init("../inputs/vardecl.tos"));
    BOOST_REQUIRE(lex.Init("../inputs/varinit.tos"));
}

BOOST_AUTO_TEST_CASE( GetNextTokenVarTest )
{
    Lexer lex;
    BOOST_REQUIRE(lex.Init("../inputs/vardecl.tos"));

    // var MyIntVar : Int;
    BOOST_REQUIRE_EQUAL(lex.GetNextToken(), Lexer::Token::VAR);
    BOOST_REQUIRE_EQUAL(lex.GetNextToken(), Lexer::Token::IDENTIFIER);
    BOOST_REQUIRE_EQUAL(lex.GetCurrentStr(), "MyIntVar");
    BOOST_REQUIRE_EQUAL(lex.GetNextToken(), Lexer::Token::COLON);
    BOOST_REQUIRE_EQUAL(lex.GetNextToken(), Lexer::Token::TYPE);
    BOOST_REQUIRE_EQUAL(lex.GetNextToken(), Lexer::Token::SEMI_COLON);

    //var MyBoolVar : Bool;
    BOOST_REQUIRE_EQUAL(lex.GetNextToken(), Lexer::Token::VAR);
    BOOST_REQUIRE_EQUAL(lex.GetNextToken(), Lexer::Token::IDENTIFIER);
    BOOST_REQUIRE_EQUAL(lex.GetCurrentStr(), "MyBoolVar");
    BOOST_REQUIRE_EQUAL(lex.GetNextToken(), Lexer::Token::COLON);
    BOOST_REQUIRE_EQUAL(lex.GetNextToken(), Lexer::Token::TYPE);
    BOOST_REQUIRE_EQUAL(lex.GetNextToken(), Lexer::Token::SEMI_COLON);

    // End of file
    BOOST_REQUIRE_EQUAL(lex.GetNextToken(), Lexer::Token::TOK_EOF);
}

BOOST_AUTO_TEST_CASE( GetNextTokenVarInitTest )
{
    Lexer lex;
    BOOST_REQUIRE(lex.Init("../inputs/varinit.tos"));

    // var MyIntVar : Int = 42;
    BOOST_REQUIRE_EQUAL(lex.GetNextToken(), Lexer::Token::VAR);
    BOOST_REQUIRE_EQUAL(lex.GetNextToken(), Lexer::Token::IDENTIFIER);
    BOOST_REQUIRE_EQUAL(lex.GetCurrentStr(), "MyIntVar");
    BOOST_REQUIRE_EQUAL(lex.GetNextToken(), Lexer::Token::COLON);
    BOOST_REQUIRE_EQUAL(lex.GetNextToken(), Lexer::Token::TYPE);
    BOOST_REQUIRE_EQUAL(lex.GetNextToken(), Lexer::Token::EQUAL);
    BOOST_REQUIRE_EQUAL(lex.GetNextToken(), Lexer::Token::NUMBER);
    BOOST_REQUIRE_EQUAL(lex.GetNextToken(), Lexer::Token::SEMI_COLON);

    //var MyTrueVar : Bool = True;
    BOOST_REQUIRE_EQUAL(lex.GetNextToken(), Lexer::Token::VAR);
    BOOST_REQUIRE_EQUAL(lex.GetNextToken(), Lexer::Token::IDENTIFIER);
    BOOST_REQUIRE_EQUAL(lex.GetCurrentStr(), "MyTrueVar");
    BOOST_REQUIRE_EQUAL(lex.GetNextToken(), Lexer::Token::COLON);
    BOOST_REQUIRE_EQUAL(lex.GetNextToken(), Lexer::Token::TYPE);
    BOOST_REQUIRE_EQUAL(lex.GetNextToken(), Lexer::Token::EQUAL);
    BOOST_REQUIRE_EQUAL(lex.GetNextToken(), Lexer::Token::TRUE);
    BOOST_REQUIRE_EQUAL(lex.GetNextToken(), Lexer::Token::SEMI_COLON);

    //var MyFalseVar : Bool = False;
    BOOST_REQUIRE_EQUAL(lex.GetNextToken(), Lexer::Token::VAR);
    BOOST_REQUIRE_EQUAL(lex.GetNextToken(), Lexer::Token::IDENTIFIER);
    BOOST_REQUIRE_EQUAL(lex.GetCurrentStr(), "MyFalseVar");
    BOOST_REQUIRE_EQUAL(lex.GetNextToken(), Lexer::Token::COLON);
    BOOST_REQUIRE_EQUAL(lex.GetNextToken(), Lexer::Token::TYPE);
    BOOST_REQUIRE_EQUAL(lex.GetNextToken(), Lexer::Token::EQUAL);
    BOOST_REQUIRE_EQUAL(lex.GetNextToken(), Lexer::Token::FALSE);
    BOOST_REQUIRE_EQUAL(lex.GetNextToken(), Lexer::Token::SEMI_COLON);

    // End of file
    BOOST_REQUIRE_EQUAL(lex.GetNextToken(), Lexer::Token::TOK_EOF);
}
<commit_msg>TosLang<commit_after>#ifdef STAND_ALONE
#   define BOOST_TEST_MODULE Main
#else
#ifndef _WIN32
#   define BOOST_TEST_MODULE Lexer
#endif
#endif

#include <boost/test/unit_test.hpp>

#include "lexer.h"

using namespace TosLang::FrontEnd;

BOOST_AUTO_TEST_CASE( InitTest )
{
    Lexer lex;
    BOOST_REQUIRE(!lex.Init("BadFile.tos"));
    BOOST_REQUIRE(lex.Init("../inputs/vardecl.tos"));
    BOOST_REQUIRE(lex.Init("../inputs/varinit.tos"));
}

BOOST_AUTO_TEST_CASE( GetNextTokenVarTest )
{
    Lexer lex;
    BOOST_REQUIRE(lex.Init("../inputs/vardecl.tos"));

    // var MyIntVar : Int;
    BOOST_REQUIRE_EQUAL(lex.GetNextToken(), Lexer::Token::VAR);
    BOOST_REQUIRE_EQUAL(lex.GetNextToken(), Lexer::Token::IDENTIFIER);
    BOOST_REQUIRE_EQUAL(lex.GetCurrentStr(), "MyIntVar");
    BOOST_REQUIRE_EQUAL(lex.GetNextToken(), Lexer::Token::COLON);
    BOOST_REQUIRE_EQUAL(lex.GetNextToken(), Lexer::Token::TYPE);
    BOOST_REQUIRE_EQUAL(lex.GetNextToken(), Lexer::Token::SEMI_COLON);

    //var MyBoolVar : Bool;
    BOOST_REQUIRE_EQUAL(lex.GetNextToken(), Lexer::Token::VAR);
    BOOST_REQUIRE_EQUAL(lex.GetNextToken(), Lexer::Token::IDENTIFIER);
    BOOST_REQUIRE_EQUAL(lex.GetCurrentStr(), "MyBoolVar");
    BOOST_REQUIRE_EQUAL(lex.GetNextToken(), Lexer::Token::COLON);
    BOOST_REQUIRE_EQUAL(lex.GetNextToken(), Lexer::Token::TYPE);
    BOOST_REQUIRE_EQUAL(lex.GetNextToken(), Lexer::Token::SEMI_COLON);

    // End of file
    BOOST_REQUIRE_EQUAL(lex.GetNextToken(), Lexer::Token::TOK_EOF);
}

BOOST_AUTO_TEST_CASE( GetNextTokenVarInitTest )
{
    Lexer lex;
    BOOST_REQUIRE(lex.Init("../inputs/varinit.tos"));

    // var MyIntVar : Int = 42;
    BOOST_REQUIRE_EQUAL(lex.GetNextToken(), Lexer::Token::VAR);
    BOOST_REQUIRE_EQUAL(lex.GetNextToken(), Lexer::Token::IDENTIFIER);
    BOOST_REQUIRE_EQUAL(lex.GetCurrentStr(), "MyIntVar");
    BOOST_REQUIRE_EQUAL(lex.GetNextToken(), Lexer::Token::COLON);
    BOOST_REQUIRE_EQUAL(lex.GetNextToken(), Lexer::Token::TYPE);
    BOOST_REQUIRE_EQUAL(lex.GetNextToken(), Lexer::Token::EQUAL);
    BOOST_REQUIRE_EQUAL(lex.GetNextToken(), Lexer::Token::NUMBER);
    BOOST_REQUIRE_EQUAL(lex.GetCurrentNumber(), 42);
    BOOST_REQUIRE_EQUAL(lex.GetNextToken(), Lexer::Token::SEMI_COLON);

    //var MyTrueVar : Bool = True;
    BOOST_REQUIRE_EQUAL(lex.GetNextToken(), Lexer::Token::VAR);
    BOOST_REQUIRE_EQUAL(lex.GetNextToken(), Lexer::Token::IDENTIFIER);
    BOOST_REQUIRE_EQUAL(lex.GetCurrentStr(), "MyTrueVar");
    BOOST_REQUIRE_EQUAL(lex.GetNextToken(), Lexer::Token::COLON);
    BOOST_REQUIRE_EQUAL(lex.GetNextToken(), Lexer::Token::TYPE);
    BOOST_REQUIRE_EQUAL(lex.GetNextToken(), Lexer::Token::EQUAL);
    BOOST_REQUIRE_EQUAL(lex.GetNextToken(), Lexer::Token::TRUE);
    BOOST_REQUIRE_EQUAL(lex.GetNextToken(), Lexer::Token::SEMI_COLON);

    //var MyFalseVar : Bool = False;
    BOOST_REQUIRE_EQUAL(lex.GetNextToken(), Lexer::Token::VAR);
    BOOST_REQUIRE_EQUAL(lex.GetNextToken(), Lexer::Token::IDENTIFIER);
    BOOST_REQUIRE_EQUAL(lex.GetCurrentStr(), "MyFalseVar");
    BOOST_REQUIRE_EQUAL(lex.GetNextToken(), Lexer::Token::COLON);
    BOOST_REQUIRE_EQUAL(lex.GetNextToken(), Lexer::Token::TYPE);
    BOOST_REQUIRE_EQUAL(lex.GetNextToken(), Lexer::Token::EQUAL);
    BOOST_REQUIRE_EQUAL(lex.GetNextToken(), Lexer::Token::FALSE);
    BOOST_REQUIRE_EQUAL(lex.GetNextToken(), Lexer::Token::SEMI_COLON);

    // End of file
    BOOST_REQUIRE_EQUAL(lex.GetNextToken(), Lexer::Token::TOK_EOF);
}
<|endoftext|>


<commit_before>/*
 * Adplug - Replayer for many OPL2/OPL3 audio file formats.
 * Copyright (C) 1999 - 2003 Simon Peter, <dn.tlp@gmx.net>, et al.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * 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
 *
 * amd.cpp - AMD Loader by Simon Peter <dn.tlp@gmx.net>
 */

#include <string.h>

#include "amd.h"
#include "debug.h"

CPlayer *CamdLoader::factory(Copl *newopl)
{
  return new CamdLoader(newopl);
}

bool CamdLoader::load(const std::string &filename, const CFileProvider &fp)
{
        binistream *f = fp.open(filename); if(!f) return false;
	struct {
		char id[9];
		unsigned char version;
	} header;
	int i, j, k, t, numtrax;
	unsigned char buf, buf2, buf3;
	const unsigned char convfx[10] = {0,1,2,9,17,11,13,18,3,14};

	// file validation section
	if(fp.filesize(f) < 1072) { fp.close(f); return false; }
	f->seek(1062); f->readString(header.id, 9);
	header.version = f->readInt(1);
	if(strncmp(header.id, "<o\xefQU\xeeRoR", 9) &&
	   strncmp(header.id, "MaDoKaN96", 9)) { fp.close(f); return false; }

	// load section
	memset(inst, 0, sizeof(inst));
	f->seek(0);
	f->readString(songname, sizeof(songname));
	f->readString(author, sizeof(author));
	for(i = 0; i < 26; i++) {
		f->readString(instname[i], 23);
		for(j = 0; j < 11; j++) inst[i].data[j] = f->readInt(1);
	}
	length = f->readInt(1); nop = f->readInt(1) + 1;
	for(i=0;i<128;i++) order[i] = f->readInt(1);
	f->seek(10, binio::Add);
	if(header.version == 0x10) {	// unpacked module
		for(i=0;i<64*9;i++)
			trackord[i/9][i%9] = i+1;
		t = 0;
		while(!f->ateof()) {
			for(j=0;j<64;j++)
				for(i=t;i<t+9;i++) {
					buf = f->readInt(1);
					tracks[i][j].param2 = (buf&127) % 10;
					tracks[i][j].param1 = (buf&127) / 10;
					buf = f->readInt(1);
					tracks[i][j].inst = buf >> 4;
					tracks[i][j].command = buf & 0x0f;
					buf = f->readInt(1);
					if(buf >> 4)	// fix bug in AMD save routine
						tracks[i][j].note = ((buf & 14) >> 1) * 12 + (buf >> 4);
					else
						tracks[i][j].note = 0;
					tracks[i][j].inst += (buf & 1) << 4;
				}
			t += 9;
		}
	} else {						// packed module
		for(i=0;i<nop;i++)
		  for(j=0;j<9;j++)
		    trackord[i][j] = f->readInt(2) + 1;
		numtrax = f->readInt(2);
		for(k=0;k<numtrax;k++) {
			i = f->readInt(2);
			if(i > 575) i = 575;	// fix corrupted modules
			j = 0;
			do {
				buf = f->readInt(1);
				if(buf & 128) {
					for(t = j; t < j + (buf & 127) && t < 64; t++) {
						tracks[i][t].command = 0;
						tracks[i][t].inst = 0;
						tracks[i][t].note = 0;
						tracks[i][t].param1 = 0;
						tracks[i][t].param2 = 0;
					}
					j += buf & 127;
					continue;
				}
				tracks[i][j].param2 = buf % 10;
				tracks[i][j].param1 = buf / 10;
				buf = f->readInt(1);
				tracks[i][j].inst = buf >> 4;
				tracks[i][j].command = buf & 0x0f;
				buf = f->readInt(1);
				if(buf >> 4)	// fix bug in AMD save routine
					tracks[i][j].note = ((buf & 14) >> 1) * 12 + (buf >> 4);
				else
					tracks[i][j].note = 0;
				tracks[i][j].inst += (buf & 1) << 4;
				j++;
			} while(j<64);
		}
	}
	fp.close(f);

	// convert to protracker replay data
	bpm = 50; restartpos = 0; activechan = 0xffff; flags = Decimal;
	for(i=0;i<26;i++) {	// convert instruments
		buf = inst[i].data[0];
		buf2 = inst[i].data[1];
		inst[i].data[0] = inst[i].data[10];
		inst[i].data[1] = buf;
		buf = inst[i].data[2];
		inst[i].data[2] = inst[i].data[5];
		buf3 = inst[i].data[3];
		inst[i].data[3] = buf;
		buf = inst[i].data[4];
		inst[i].data[4] = inst[i].data[7];
		inst[i].data[5] = buf3;
		buf3 = inst[i].data[6];
		inst[i].data[6] = inst[i].data[8];
		inst[i].data[7] = buf;
		inst[i].data[8] = inst[i].data[9];
		inst[i].data[9] = buf2;
		inst[i].data[10] = buf3;
		for(j=0;j<23;j++)	// convert names
			if(instname[i][j] == '\xff')
				instname[i][j] = '\x20';
	}
	for(i=0;i<nop*9;i++)	// convert patterns
		for(j=0;j<64;j++) {
			tracks[i][j].command = convfx[tracks[i][j].command];
			if(tracks[i][j].command == 14) {
				if(tracks[i][j].param1 == 2) {
					tracks[i][j].command = 10;
					tracks[i][j].param1 = tracks[i][j].param2;
					tracks[i][j].param2 = 0;
				}
				if(tracks[i][j].param1 == 3) {
					tracks[i][j].command = 10;
					tracks[i][j].param1 = 0;
				}
			}
		}

	rewind(0);
	return true;
}

float CamdLoader::getrefresh()
{
	if(tempo)
		return (float) (tempo);
	else
		return 18.2f;
}
<commit_msg>Fixed command conversion. Old algorithm did not always convert the whole song.<commit_after>/*
 * Adplug - Replayer for many OPL2/OPL3 audio file formats.
 * Copyright (C) 1999 - 2006 Simon Peter, <dn.tlp@gmx.net>, et al.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * 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
 *
 * amd.cpp - AMD Loader by Simon Peter <dn.tlp@gmx.net>
 */

#include <string.h>

#include "amd.h"
#include "debug.h"

CPlayer *CamdLoader::factory(Copl *newopl)
{
  return new CamdLoader(newopl);
}

bool CamdLoader::load(const std::string &filename, const CFileProvider &fp)
{
  binistream *f = fp.open(filename); if(!f) return false;
  struct {
    char id[9];
    unsigned char version;
  } header;
  int i, j, k, t, numtrax, maxi = 0;
  unsigned char buf, buf2, buf3;
  const unsigned char convfx[10] = {0,1,2,9,17,11,13,18,3,14};

  // file validation section
  if(fp.filesize(f) < 1072) { fp.close(f); return false; }
  f->seek(1062); f->readString(header.id, 9);
  header.version = f->readInt(1);
  if(strncmp(header.id, "<o\xefQU\xeeRoR", 9) &&
     strncmp(header.id, "MaDoKaN96", 9)) { fp.close(f); return false; }

  // load section
  memset(inst, 0, sizeof(inst));
  f->seek(0);
  f->readString(songname, sizeof(songname));
  f->readString(author, sizeof(author));
  for(i = 0; i < 26; i++) {
    f->readString(instname[i], 23);
    for(j = 0; j < 11; j++) inst[i].data[j] = f->readInt(1);
  }
  length = f->readInt(1); nop = f->readInt(1) + 1;
  for(i=0;i<128;i++) order[i] = f->readInt(1);
  f->seek(10, binio::Add);
  if(header.version == 0x10) {	// unpacked module
    maxi = nop * 9;
    for(i=0;i<64*9;i++)
      trackord[i/9][i%9] = i+1;
    t = 0;
    while(!f->ateof()) {
      for(j=0;j<64;j++)
	for(i=t;i<t+9;i++) {
	  buf = f->readInt(1);
	  tracks[i][j].param2 = (buf&127) % 10;
	  tracks[i][j].param1 = (buf&127) / 10;
	  buf = f->readInt(1);
	  tracks[i][j].inst = buf >> 4;
	  tracks[i][j].command = buf & 0x0f;
	  buf = f->readInt(1);
	  if(buf >> 4)	// fix bug in AMD save routine
	    tracks[i][j].note = ((buf & 14) >> 1) * 12 + (buf >> 4);
	  else
	    tracks[i][j].note = 0;
	  tracks[i][j].inst += (buf & 1) << 4;
	}
      t += 9;
    }
  } else {			// packed module
    for(i=0;i<nop;i++)
      for(j=0;j<9;j++)
	trackord[i][j] = f->readInt(2) + 1;
    numtrax = f->readInt(2);
    for(k=0;k<numtrax;k++) {
      i = f->readInt(2);
      if(i > 575) i = 575;	// fix corrupted modules
      maxi = (i + 1 > maxi ? i + 1 : maxi);
      j = 0;
      do {
	buf = f->readInt(1);
	if(buf & 128) {
	  for(t = j; t < j + (buf & 127) && t < 64; t++) {
	    tracks[i][t].command = 0;
	    tracks[i][t].inst = 0;
	    tracks[i][t].note = 0;
	    tracks[i][t].param1 = 0;
	    tracks[i][t].param2 = 0;
	  }
	  j += buf & 127;
	  continue;
	}
	tracks[i][j].param2 = buf % 10;
	tracks[i][j].param1 = buf / 10;
	buf = f->readInt(1);
	tracks[i][j].inst = buf >> 4;
	tracks[i][j].command = buf & 0x0f;
	buf = f->readInt(1);
	if(buf >> 4)	// fix bug in AMD save routine
	  tracks[i][j].note = ((buf & 14) >> 1) * 12 + (buf >> 4);
	else
	  tracks[i][j].note = 0;
	tracks[i][j].inst += (buf & 1) << 4;
	j++;
      } while(j<64);
    }
  }
  fp.close(f);

  // convert to protracker replay data
  bpm = 50; restartpos = 0; activechan = 0xffff; flags = Decimal;
  for(i=0;i<26;i++) {	// convert instruments
    buf = inst[i].data[0];
    buf2 = inst[i].data[1];
    inst[i].data[0] = inst[i].data[10];
    inst[i].data[1] = buf;
    buf = inst[i].data[2];
    inst[i].data[2] = inst[i].data[5];
    buf3 = inst[i].data[3];
    inst[i].data[3] = buf;
    buf = inst[i].data[4];
    inst[i].data[4] = inst[i].data[7];
    inst[i].data[5] = buf3;
    buf3 = inst[i].data[6];
    inst[i].data[6] = inst[i].data[8];
    inst[i].data[7] = buf;
    inst[i].data[8] = inst[i].data[9];
    inst[i].data[9] = buf2;
    inst[i].data[10] = buf3;
    for(j=0;j<23;j++)	// convert names
      if(instname[i][j] == '\xff')
	instname[i][j] = '\x20';
  }
  for(i=0;i<maxi;i++)	// convert patterns
    for(j=0;j<64;j++) {
      tracks[i][j].command = convfx[tracks[i][j].command];
      // extended command
      if(tracks[i][j].command == 14) {
	if(tracks[i][j].param1 == 2) {
	  tracks[i][j].command = 10;
	  tracks[i][j].param1 = tracks[i][j].param2;
	  tracks[i][j].param2 = 0;
	}

	if(tracks[i][j].param1 == 3) {
	  tracks[i][j].command = 10;
	  tracks[i][j].param1 = 0;
	}
      }
    }

  rewind(0);
  return true;
}

float CamdLoader::getrefresh()
{
  if(tempo)
    return (float) (tempo);
  else
    return 18.2f;
}
<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: setactivity.hxx,v $
 *
 *  $Revision: 1.6 $
 *
 *  last change: $Author: obo $ $Date: 2005-10-11 08:45:15 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, 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
 *
 ************************************************************************/
#ifndef INCLUDED_SLIDESHOW_SETACTIVITY_HXX
#define INCLUDED_SLIDESHOW_SETACTIVITY_HXX

// must be first
#include "canvas/debug.hxx"
#include "canvas/verbosetrace.hxx"
#include "animationactivity.hxx"
#include "animation.hxx"
#include "animatableshape.hxx"
#include "shapeattributelayer.hxx"
#include "activitiesfactory.hxx"

namespace presentation {
namespace internal {

/** Templated setter for animation values

    This template class implements the AnimationActivity
    interface, but only the perform() and
    setAttributeLayer() methods are functional. To be used for set animations.

    @see AnimationSetNode.
*/
template <class AnimationT>
class SetActivity : public AnimationActivity
{
public:
    typedef ::boost::shared_ptr< AnimationT >   AnimationSharedPtrT;
    typedef typename AnimationT::ValueType      ValueT;

    SetActivity( const ActivitiesFactory::CommonParameters& rParms,
                 const AnimationSharedPtrT&                 rAnimation,
                 const ValueT&                              rToValue )
        : mpAnimation( rAnimation ),
          mpShape(),
          mpAttributeLayer(),
          mpEndEvent( rParms.mpEndEvent ),
          mrEventQueue( rParms.mrEventQueue ),
          maToValue( rToValue ),
          mbIsActive(true)
    {
        ENSURE_AND_THROW( mpAnimation.get(), "Invalid animation" );
    }

    virtual void dispose()
    {
        mbIsActive = false;
        mpAnimation.reset();
        mpShape.reset();
        mpAttributeLayer.reset();
        // discharge end event:
        if (mpEndEvent && mpEndEvent->isCharged())
            mpEndEvent->dispose();
        mpEndEvent.reset();
    }

    virtual double calcTimeLag() const
    {
        return 0.0;
    }

    virtual bool perform()
    {
        if (! isActive())
            return false;
        // we're going inactive immediately:
        mbIsActive = false;

        if (mpAnimation && mpAttributeLayer && mpShape) {
            mpAnimation->start( mpShape, mpAttributeLayer );
            (*mpAnimation)(maToValue);
            mpAnimation->end();
        }
        // fire end event, if any
        if (mpEndEvent)
            mrEventQueue.addEvent( mpEndEvent );

        return false; // don't reinsert
    }

    virtual bool isActive() const
    {
        return mbIsActive;
    }

    virtual bool needsScreenUpdate() const
    {
        return true;
    }

    virtual void dequeued()
    {
    }

    virtual void end()
    {
        perform();
    }

    virtual void setTargets( const AnimatableShapeSharedPtr&        rShape,
                             const ShapeAttributeLayerSharedPtr&    rAttrLayer )
    {
        ENSURE_AND_THROW( rShape.get(), "Invalid shape" );
        ENSURE_AND_THROW( rAttrLayer.get(), "Invalid attribute layer" );

        mpShape = rShape;
        mpAttributeLayer = rAttrLayer;
    }

private:
    AnimationSharedPtrT             mpAnimation;
    AnimatableShapeSharedPtr        mpShape;
    ShapeAttributeLayerSharedPtr    mpAttributeLayer;
    EventSharedPtr                  mpEndEvent;
    EventQueue&                     mrEventQueue;
    ValueT                          maToValue;
    bool                            mbIsActive;
};

template <class AnimationT> AnimationActivitySharedPtr makeSetActivity(
    const ActivitiesFactory::CommonParameters& rParms,
    const ::boost::shared_ptr< AnimationT >&   rAnimation,
    const typename AnimationT::ValueType&      rToValue )
{
    return AnimationActivitySharedPtr(
        new SetActivity<AnimationT>(rParms,rAnimation,rToValue) );
}

} // namespace internal
} // namespace presentation

#endif /* INCLUDED_SLIDESHOW_SETACTIVITY_HXX */
<commit_msg>INTEGRATION: CWS presfixes09 (1.6.16); FILE MERGED 2006/04/24 13:25:29 thb 1.6.16.3: #i53194# Unified include statements (local headers always have double quotes; external headers angle brackets); reverted EventMultiplexer pause events to shared_ptr; removed EventMultiplexer::removeViewHandler(), since the handler is held weakly, anyway. 2006/04/12 20:40:08 thb 1.6.16.2: #i37778# Replaced all shared_ptr.get() != NULL places with the more elegant automatic-conversion-to-bool version (at least where the compiler tolerated that) 2006/03/24 18:23:20 thb 1.6.16.1: #i37778# Moved whole slideshow engine from namespace presentation (which conflicts with one of the UNO subnamespaces) to slideshow<commit_after>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: setactivity.hxx,v $
 *
 *  $Revision: 1.7 $
 *
 *  last change: $Author: kz $ $Date: 2006-12-13 15:35:35 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, 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
 *
 ************************************************************************/
#ifndef INCLUDED_SLIDESHOW_SETACTIVITY_HXX
#define INCLUDED_SLIDESHOW_SETACTIVITY_HXX

// must be first
#include <canvas/debug.hxx>
#include <canvas/verbosetrace.hxx>

#include "animationactivity.hxx"
#include "animation.hxx"
#include "animatableshape.hxx"
#include "shapeattributelayer.hxx"
#include "activitiesfactory.hxx"

namespace slideshow {
namespace internal {

/** Templated setter for animation values

    This template class implements the AnimationActivity
    interface, but only the perform() and
    setAttributeLayer() methods are functional. To be used for set animations.

    @see AnimationSetNode.
*/
template <class AnimationT>
class SetActivity : public AnimationActivity
{
public:
    typedef ::boost::shared_ptr< AnimationT >   AnimationSharedPtrT;
    typedef typename AnimationT::ValueType      ValueT;

    SetActivity( const ActivitiesFactory::CommonParameters& rParms,
                 const AnimationSharedPtrT&                 rAnimation,
                 const ValueT&                              rToValue )
        : mpAnimation( rAnimation ),
          mpShape(),
          mpAttributeLayer(),
          mpEndEvent( rParms.mpEndEvent ),
          mrEventQueue( rParms.mrEventQueue ),
          maToValue( rToValue ),
          mbIsActive(true)
    {
        ENSURE_AND_THROW( mpAnimation, "Invalid animation" );
    }

    virtual void dispose()
    {
        mbIsActive = false;
        mpAnimation.reset();
        mpShape.reset();
        mpAttributeLayer.reset();
        // discharge end event:
        if (mpEndEvent && mpEndEvent->isCharged())
            mpEndEvent->dispose();
        mpEndEvent.reset();
    }

    virtual double calcTimeLag() const
    {
        return 0.0;
    }

    virtual bool perform()
    {
        if (! isActive())
            return false;
        // we're going inactive immediately:
        mbIsActive = false;

        if (mpAnimation && mpAttributeLayer && mpShape) {
            mpAnimation->start( mpShape, mpAttributeLayer );
            (*mpAnimation)(maToValue);
            mpAnimation->end();
        }
        // fire end event, if any
        if (mpEndEvent)
            mrEventQueue.addEvent( mpEndEvent );

        return false; // don't reinsert
    }

    virtual bool isActive() const
    {
        return mbIsActive;
    }

    virtual bool needsScreenUpdate() const
    {
        return true;
    }

    virtual void dequeued()
    {
    }

    virtual void end()
    {
        perform();
    }

    virtual void setTargets( const AnimatableShapeSharedPtr&        rShape,
                             const ShapeAttributeLayerSharedPtr&    rAttrLayer )
    {
        ENSURE_AND_THROW( rShape, "Invalid shape" );
        ENSURE_AND_THROW( rAttrLayer, "Invalid attribute layer" );

        mpShape = rShape;
        mpAttributeLayer = rAttrLayer;
    }

private:
    AnimationSharedPtrT             mpAnimation;
    AnimatableShapeSharedPtr        mpShape;
    ShapeAttributeLayerSharedPtr    mpAttributeLayer;
    EventSharedPtr                  mpEndEvent;
    EventQueue&                     mrEventQueue;
    ValueT                          maToValue;
    bool                            mbIsActive;
};

template <class AnimationT> AnimationActivitySharedPtr makeSetActivity(
    const ActivitiesFactory::CommonParameters& rParms,
    const ::boost::shared_ptr< AnimationT >&   rAnimation,
    const typename AnimationT::ValueType&      rToValue )
{
    return AnimationActivitySharedPtr(
        new SetActivity<AnimationT>(rParms,rAnimation,rToValue) );
}

} // namespace internal
} // namespace presentation

#endif /* INCLUDED_SLIDESHOW_SETACTIVITY_HXX */
<|endoftext|>


<commit_before>/*******************************************************************************
 * Copyright (c) 2012, Dougal J. Sutherland (dsutherl@cs.cmu.edu).             *
 * All rights reserved.                                                        *
 *                                                                             *
 * Redistribution and use in source and binary forms, with or without          *
 * modification, are permitted provided that the following conditions are met: *
 *                                                                             *
 *     * Redistributions of source code must retain the above copyright        *
 *       notice, this list of conditions and the following disclaimer.         *
 *                                                                             *
 *     * Redistributions in binary form must reproduce the above copyright     *
 *       notice, this list of conditions and the following disclaimer in the   *
 *       documentation and/or other materials provided with the distribution.  *
 *                                                                             *
 *     * Neither the name of Carnegie Mellon University nor the                *
 *       names of the contributors may be used to endorse or promote products  *
 *       derived from this software without specific prior written permission. *
 *                                                                             *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" *
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE   *
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE  *
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE   *
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR         *
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF        *
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS    *
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN     *
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)     *
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE  *
 * POSSIBILITY OF SUCH DAMAGE.                                                 *
 ******************************************************************************/
#include "sdm/basics.hpp"
#include "sdm/sdm.hpp"
#include "sdm/kernels/linear.hpp"
#include "sdm/kernels/polynomial.hpp"
#include "sdm/kernels/gaussian.hpp"

#include <algorithm>
#include <cstdlib>
#include <fstream>
#include <iostream>
#include <stdexcept>
#include <string>
#include <vector>

#include <boost/bind.hpp>
#include <boost/format.hpp>
#include <boost/program_options.hpp>
#include <boost/utility.hpp>

#include <flann/flann.hpp>

#include <np-divs/matrix_io.hpp>
#include <np-divs/div-funcs/from_str.hpp>

// TODO: more general CLI (multiclass, etc)
//       probably switch to a "train_bags" that includes labels

using std::cerr;
using std::cin;
using std::cout;
using std::ifstream;
using std::endl;
using std::string;

namespace po = boost::program_options;

// TODO: leaks memory everywhere, but whatever...
struct ProgOpts : boost::noncopyable {
    string pos_bags_file;
    string neg_bags_file;

    string test_bags_file;
    string test_labels_file;

    npdivs::DivFunc * div_func;
    sdm::KernelGroup * kernel_group;

    size_t k;
    size_t num_threads;
    size_t tuning_folds;
    bool prob;

    flann::IndexParams * index_params;
    flann::SearchParams * search_params;

    void parse_div_func(const string spec) {
        div_func = npdivs::div_func_from_str(spec);
    }

    void parse_kernel(const string name) {
        // TODO: support specifying cs / sigmas / degrees / etc
        if (name == "gaussian") {
            kernel_group = new sdm::GaussianKernelGroup;
        } else if (name == "linear") {
            kernel_group = new sdm::LinearKernelGroup;
        } else if (name == "polynomial") {
            kernel_group = new sdm::PolynomialKernelGroup;
        } else {
            throw std::domain_error((
                        boost::format("unknown kernel type %s") % name).str());
        }
    }

    void parse_index(const string name) {
        // TODO: more index types, support arguments
        if (name == "linear" || name == "brute") {
            index_params = new flann::LinearIndexParams;

        } else if (name == "kdtree" || name == "kd") {
            index_params = new flann::KDTreeSingleIndexParams;

        } else {
            throw std::domain_error((
                        boost::format("unknown index type %s") % name).str());
        }
    }
};

bool parse_args(int argc, char ** argv, ProgOpts& opts);

int main(int argc, char ** argv) {
    typedef flann::Matrix<double> Matrix;

    try {
        ProgOpts opts;
        if (!parse_args(argc, argv, opts))
            return 1;

        // TODO: gracefully handle nonexisting files
        // TODO: more robust input checking

        // load positives
        size_t num_pos;
        Matrix* pos_bags;
        if (opts.pos_bags_file == "-") {
            cout << "Enter positive training distributions in CSV-like "
                "format: one line with comma-separated values for each "
                "point, one blank line between distributions, and an extra "
                "blank line when done.\n";
            pos_bags = npdivs::matrices_from_csv(std::cin, num_pos);
        } else {
            ifstream ifs(opts.pos_bags_file.c_str(), ifstream::in);
            pos_bags = npdivs::matrices_from_csv(ifs, num_pos);
        }

        // load negatives
        size_t num_neg;
        Matrix* neg_bags;
        if (opts.neg_bags_file == "-") {
            cout << "Enter negative training distributions in CSV-like "
                "format: one line with comma-separated values for each "
                "point, one blank line between distributions, and an extra "
                "blank line when done.\n";
            neg_bags = npdivs::matrices_from_csv(cin, num_neg);
        } else {
            ifstream ifs(opts.neg_bags_file.c_str(), ifstream::in);
            neg_bags = npdivs::matrices_from_csv(ifs, num_neg);
        }

        // combine training bags - TODO: this leaks memory
        Matrix* train_bags = new Matrix[num_pos + num_neg];
        for (size_t i = 0; i < num_pos; i++)
            train_bags[i] = pos_bags[i];
        for (size_t i = 0; i < num_neg; i++)
            train_bags[num_pos + i] = neg_bags[i];

        // load test
        size_t num_test;
        Matrix* test_bags;
        if (opts.test_bags_file == "-") {
            cout << "Enter testing distributions in CSV-like "
                "format: one line with comma-separated values for each "
                "point, one blank line between distributions, and an extra "
                "blank line when done.\n";
            test_bags = npdivs::matrices_from_csv(cin, num_test);
        } else {
            ifstream ifs(opts.test_bags_file.c_str(), ifstream::in);
            test_bags = npdivs::matrices_from_csv(ifs, num_test);
        }

        // load test labels, maybe
        std::vector<int> test_labels;
        if (opts.test_labels_file == "-") {
            cout << "Input test labels, separated by whitespace: ";
            test_labels.resize(num_test);
            for (size_t i = 0; i < num_test; i++)
                cin >> test_labels[i];
        } else if (opts.test_labels_file != "") {
            ifstream ifs(opts.test_labels_file.c_str(), ifstream::in);

            test_labels.resize(num_test);
            for (size_t i = 0; i < num_test; i++)
                ifs >> test_labels[i];
        }

        // set up labels
        std::vector<int> labels(num_pos + num_neg, 0); // all 0s
        std::fill_n(labels.begin(), num_pos, 1); // 1s then 0s

        // div params
        npdivs::DivParams div_params(opts.k,
                *opts.index_params, *opts.search_params,
                opts.num_threads);

        // svm params
        svm_parameter svm_params(sdm::default_svm_params);
        svm_params.probability = (int) opts.prob;

        // train the model
        sdm::SDM<double>* model = train_sdm(
                train_bags, num_pos + num_neg, labels,
                *opts.div_func, *opts.kernel_group, div_params,
                sdm::default_c_vals, svm_params, opts.tuning_folds);

        // predict on test data
        const std::vector<int> &preds = model->predict(test_bags, num_test);

        // output predictions // TODO: optionally into file?
        cout << "Predicted labels: ";
        for (size_t i = 0; i < num_test; i++) {
            cout << preds[i] << " ";
        }
        cout << endl;

        // test accuracy, if available
        if (test_labels.size() > 0) {
            size_t num_correct = 0;
            for (size_t i = 0; i < num_test; i++)
                if (preds[i] == test_labels[i])
                    num_correct++;
            cout << "Accuracy: " << num_correct * 100. / num_test << "%\n";
        }

        // clean up
        model->destroyModelAndProb();
        delete model;
        delete[] train_bags;

    } catch (std::exception &e) {
        cerr << "Error: " << e.what() << endl;
        return 1;
    }
}

bool parse_args(int argc, char ** argv, ProgOpts& opts) {
    po::options_description desc("Allowed options");
    desc.add_options()
        ("help,h", "Produce this help message.")
        ("pos-bags,p",
            po::value<string>(&opts.pos_bags_file)->default_value("-"),
            "CSV-style file containing matrices separated by blank lines; "
            "- means stdin.")
        ("neg-bags,n",
            po::value<string>(&opts.neg_bags_file)->default_value("-"),
            "CSV-style file containing matrices separated by blank lines; "
            "- means stdin; if both pos and neg are read from stdin, pos "
            "is read first and the two groups must be separated by exactly "
            "two blank lines.")
        ("test-bags,t",
            po::value<string>(&opts.test_bags_file)->default_value("-"),
            "CSV-style file containing matrices separated by blank lines; "
            "- means stdin; again must be separated from previous groups by "
            "exactly two blank lines.")
        ("test-labels,l",
            po::value<string>(&opts.test_labels_file)->default_value(""),
            "A file containing labels for the test distributions, in the "
            "same order, one per line. Used to print out test accuracy; "
            "- means stdin, separate by exactly two blank lines.")
        ("div-func,d",
            po::value<string>()->default_value("l2")->notifier(
                boost::bind(&ProgOpts::parse_div_func, boost::ref(opts), _1)),
            "Divergence function to use. Format is name[:arg1[:arg2...]]. "
            "Options include alpha, bc, hellinger, l2, linear, renyi. "
            "alpha and renyi take an optional second argument, so that "
            "e.g. renyi:.8 is the Renyi-.8 divergence. All options take a "
            "last argument that specifies how large intermediate values are "
            "normalized; the default .99 means to cap certain values at the "
            "99th percentile of their distribution, and 1 means not to do "
            " this.")
        ("kernel,k",
            po::value<string>()->default_value("gaussian")->notifier(
                boost::bind(&ProgOpts::parse_kernel, boost::ref(opts), _1)),
            "Kernel type to use. Options are gaussian, linear, polynomial. "
            "Note that cross-validation is done to select gaussian kernel "
            "width or polynomial degree, in combination with SVM C; this is "
            "not yet configurable through this interface.")
        ("tuning-folds,f",
            po::value<size_t>(&opts.tuning_folds)->default_value(3),
            "The number of folds to use for the parameter tuning "
            "cross-validation.")
        ("probability,P",
            po::value<bool>(&opts.prob)->zero_tokens(),
            "Use probability estimates in the trained SVMs.")
        ("num-threads,T",
            po::value<size_t>(&opts.num_threads)->default_value(0),
            "Number of threads to use for calculations. 0 means one per core "
            "if compiled with recent-enough boost, or one thread otherwise.")
        ("neighbors,K",
            po::value<size_t>(&opts.k)->default_value(3),
            "The k for k-nearest-neighbor calculations.")
        ("index,i",
            po::value<string>()->default_value("kdtree")->notifier(
                boost::bind(&ProgOpts::parse_index, boost::ref(opts), _1)),
            "The nearest-neighbor index to use. Options: linear, kdtree. "
            "Note that this can have a large effect on calculation time: "
            "use kdtree for low-dimensional data and linear for relatively "
            "sparse high-dimensional data (about about 10).")
    ;

    po::variables_map vm;
    try {
        po::store(po::command_line_parser(argc, argv).options(desc).run(), vm);

        if (vm.count("help")) {
            cout << desc << endl;
            std::exit(0);
        }

        po::notify(vm);
    } catch (std::exception &e) {
        cerr << "Error: " << e.what() << endl;
        return false;
    }

    opts.search_params = new flann::SearchParams(64);

    return true;
}
<commit_msg>CLI: switch to labeled matrix format<commit_after>/*******************************************************************************
 * Copyright (c) 2012, Dougal J. Sutherland (dsutherl@cs.cmu.edu).             *
 * All rights reserved.                                                        *
 *                                                                             *
 * Redistribution and use in source and binary forms, with or without          *
 * modification, are permitted provided that the following conditions are met: *
 *                                                                             *
 *     * Redistributions of source code must retain the above copyright        *
 *       notice, this list of conditions and the following disclaimer.         *
 *                                                                             *
 *     * Redistributions in binary form must reproduce the above copyright     *
 *       notice, this list of conditions and the following disclaimer in the   *
 *       documentation and/or other materials provided with the distribution.  *
 *                                                                             *
 *     * Neither the name of Carnegie Mellon University nor the                *
 *       names of the contributors may be used to endorse or promote products  *
 *       derived from this software without specific prior written permission. *
 *                                                                             *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" *
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE   *
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE  *
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE   *
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR         *
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF        *
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS    *
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN     *
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)     *
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE  *
 * POSSIBILITY OF SUCH DAMAGE.                                                 *
 ******************************************************************************/
#include "sdm/basics.hpp"
#include "sdm/sdm.hpp"
#include "sdm/kernels/linear.hpp"
#include "sdm/kernels/polynomial.hpp"
#include "sdm/kernels/gaussian.hpp"

#include <algorithm>
#include <cstdlib>
#include <fstream>
#include <iostream>
#include <map>
#include <stdexcept>
#include <string>
#include <vector>

#include <boost/bind.hpp>
#include <boost/format.hpp>
#include <boost/program_options.hpp>
#include <boost/utility.hpp>

#include <flann/flann.hpp>

#include <np-divs/matrix_io.hpp>
#include <np-divs/div-funcs/from_str.hpp>

// TODO: support CV
// TODO: warn about dumb parameter combos, like linear kernel with distance df

using std::cerr;
using std::cin;
using std::cout;
using std::endl;
using std::ifstream;
using std::string;
using std::vector;

namespace po = boost::program_options;

// TODO: leaks memory everywhere, but whatever...
struct ProgOpts : boost::noncopyable {
    string train_bags_file;
    string test_bags_file;

    npdivs::DivFunc * div_func;
    sdm::KernelGroup * kernel_group;

    size_t k;
    size_t num_threads;
    size_t tuning_folds;
    bool prob;

    flann::IndexParams * index_params;
    flann::SearchParams * search_params;

    void parse_div_func(const string spec) {
        div_func = npdivs::div_func_from_str(spec);
    }

    void parse_kernel(const string name) {
        // TODO: support specifying cs / sigmas / degrees / etc
        if (name == "gaussian") {
            kernel_group = new sdm::GaussianKernelGroup;
        } else if (name == "linear") {
            kernel_group = new sdm::LinearKernelGroup;
        } else if (name == "polynomial") {
            kernel_group = new sdm::PolynomialKernelGroup;
        } else {
            throw std::domain_error((
                        boost::format("unknown kernel type %s") % name).str());
        }
    }

    void parse_index(const string name) {
        // TODO: more index types, support arguments
        if (name == "linear" || name == "brute") {
            index_params = new flann::LinearIndexParams;

        } else if (name == "kdtree" || name == "kd") {
            index_params = new flann::KDTreeSingleIndexParams;

        } else {
            throw std::domain_error((
                        boost::format("unknown index type %s") % name).str());
        }
    }
};

bool parse_args(int argc, char ** argv, ProgOpts& opts);

int main(int argc, char ** argv) {
    typedef flann::Matrix<double> Matrix;

    try {
        ProgOpts opts;
        if (!parse_args(argc, argv, opts))
            return 1;

        // TODO: gracefully handle nonexisting files
        // TODO: more robust input checking

        // load training bags
        size_t num_train;
        Matrix* train_bags;
        vector<string> train_labels;

        if (opts.train_bags_file == "-") {
            cout << "Enter training distributions in CSV-like "
                "format: an initial string label for each distribution, one "
                "line with comma-separated floating-point values for each "
                "point, one blank line between distributions, and an extra "
                "blank line when done.\n";
            train_bags = npdivs::labeled_matrices_from_csv(
                    std::cin, num_train, train_labels);
        } else {
            ifstream ifs(opts.train_bags_file.c_str(), ifstream::in);
            train_bags = npdivs::labeled_matrices_from_csv(
                    ifs, num_train, train_labels);
        }

        // load test
        size_t num_test;
        Matrix* test_bags;
        vector<string> test_labels;

        if (opts.test_bags_file == "-") {
            cout << "Enter testing distributions in CSV-like "
                "format: an initial string label for each distribution, one "
                "line with comma-separated floating-point values for each "
                "point, one blank line between distributions, and an extra "
                "blank line when done.\n";
            test_bags = npdivs::labeled_matrices_from_csv(
                    cin, num_test, test_labels);
        } else {
            ifstream ifs(opts.test_bags_file.c_str(), ifstream::in);
            test_bags = npdivs::labeled_matrices_from_csv(
                    ifs, num_test, test_labels);
        }

        // convert labels into integers
        // make maps to convert between int / string labels
        int this_label = -1;
        std::map<string,int> labels_to_int;
        std::map<int,string> labels_to_string;

        std::vector<int> train_labels_ints;
        train_labels_ints.reserve(num_train);
        for (size_t i = 0; i < num_train; i++) {
            const string &lbl = train_labels[i];

            if (labels_to_int.count(lbl) == 0) {
                labels_to_int[lbl] = ++this_label;
                labels_to_string[this_label] = lbl;
            }
            train_labels_ints.push_back(labels_to_int[lbl]);
        }

        std::vector<int> test_labels_ints;
        test_labels_ints.reserve(num_test);
        for (size_t i = 0; i < num_train; i++) {
            const string &lbl = test_labels[i];

            if (labels_to_int.count(lbl) == 0) {
                labels_to_int[lbl] = ++this_label;
                labels_to_string[this_label] = lbl;
            }
            test_labels_ints.push_back(labels_to_int[lbl]);
        }

        // div params
        npdivs::DivParams div_params(opts.k,
                *opts.index_params, *opts.search_params,
                opts.num_threads);

        // svm params
        svm_parameter svm_params(sdm::default_svm_params);
        svm_params.probability = (int) opts.prob;

        // train the model
        sdm::SDM<double>* model = train_sdm(
                train_bags, num_train, train_labels_ints,
                *opts.div_func, *opts.kernel_group, div_params,
                sdm::default_c_vals, svm_params, opts.tuning_folds);

        // predict on test data
        const std::vector<int> &preds = model->predict(test_bags, num_test);

        // output predictions // TODO: optionally into file?
        cout << "Predicted labels:\n";
        for (size_t i = 0; i < num_test; i++) {
            cout << labels_to_string[preds[i]] << endl;
        }

        // test accuracy, if available
        size_t num_correct = 0;
        for (size_t i = 0; i < num_test; i++)
            if (preds[i] == test_labels_ints[i])
                num_correct++;
        cout << "Accuracy: " << num_correct * 100. / num_test << "%\n";

        // clean up
        model->destroyModelAndProb();
        delete model;
        delete[] train_bags;

    } catch (std::exception &e) {
        cerr << "Error: " << e.what() << endl;
        return 1;
    }
}

bool parse_args(int argc, char ** argv, ProgOpts& opts) {
    po::options_description desc("Allowed options");
    desc.add_options()
        ("help,h", "Produce this help message.")
        ("train-bags,b",
            po::value<string>(&opts.train_bags_file)->default_value("-"),
            "CSV-like file containing matrices separated by blank lines, "
            "with a string label on its own line before each matrix; "
            "- means stdin.")
        ("test-bags,t",
            po::value<string>(&opts.test_bags_file)->default_value("-"),
            "CSV-style file containing matrices separated by blank lines; "
            "- means stdin.")
        ("div-func,d",
            po::value<string>()->default_value("l2")->notifier(
                boost::bind(&ProgOpts::parse_div_func, boost::ref(opts), _1)),
            "Divergence function to use. Format is name[:arg1[:arg2...]]. "
            "Options include alpha, bc, hellinger, l2, linear, renyi. "
            "alpha and renyi take an optional second argument, so that "
            "e.g. renyi:.8 is the Renyi-.8 divergence. All options take a "
            "last argument that specifies how large intermediate values are "
            "normalized; the default .99 means to cap certain values at the "
            "99th percentile of their distribution, and 1 means not to do "
            " this.")
        ("kernel,k",
            po::value<string>()->default_value("gaussian")->notifier(
                boost::bind(&ProgOpts::parse_kernel, boost::ref(opts), _1)),
            "Kernel type to use. Options are gaussian, linear, polynomial. "
            "Note that cross-validation is done to select gaussian kernel "
            "width or polynomial degree, in combination with SVM C; this is "
            "not yet configurable through this interface.")
        ("tuning-folds,f",
            po::value<size_t>(&opts.tuning_folds)->default_value(3),
            "The number of folds to use for the parameter tuning "
            "cross-validation.")
        ("probability,P",
            po::value<bool>(&opts.prob)->zero_tokens(),
            "Use probability estimates in the trained SVMs.")
        ("num-threads,T",
            po::value<size_t>(&opts.num_threads)->default_value(0),
            "Number of threads to use for calculations. 0 means one per core "
            "if compiled with recent-enough boost, or one thread otherwise.")
        ("neighbors,K",
            po::value<size_t>(&opts.k)->default_value(3),
            "The k for k-nearest-neighbor calculations.")
        ("index,i",
            po::value<string>()->default_value("kdtree")->notifier(
                boost::bind(&ProgOpts::parse_index, boost::ref(opts), _1)),
            "The nearest-neighbor index to use. Options: linear, kdtree. "
            "Note that this can have a large effect on calculation time: "
            "use kdtree for low-dimensional data and linear for relatively "
            "sparse high-dimensional data (about about 10).")
    ;

    po::variables_map vm;
    try {
        po::store(po::command_line_parser(argc, argv).options(desc).run(), vm);

        if (vm.count("help")) {
            cout << desc << endl;
            std::exit(0);
        }

        po::notify(vm);
    } catch (std::exception &e) {
        cerr << "Error: " << e.what() << endl;
        return false;
    }

    opts.search_params = new flann::SearchParams(64);

    return true;
}
<|endoftext|>


<commit_before>#include "data-student.hpp"
using namespace std;

void Student::init(string n, int s, int g, string m) {
	name = n;
	startingYear = s;
	gradutationYear = g;
	addMajors(m);
}

Student::Student() {
	init("", 2000, 2004, "");
}
Student::Student(string n, string s, string e, string m) {
	int startYear = stringToInt(s), endYear = stringToInt(e);
	init(n, startYear, endYear, m);
}

Student::Student(string fn) {
	string n, yearS, yearE, m, conc, c;
	ifstream infile(fn.c_str());
	string nextLine;
	vector<string> lines;

	// load the entire file
	while (infile.peek() != -1) {
		getline(infile, nextLine);
		lines.push_back(nextLine);
	}

	string previousHeading;
	for (vector<string>::iterator i = lines.begin(); i != lines.end(); ++i) {
		string str = *i;
		if (str[0] == '#') {
			previousHeading = *i;
			continue;
		}

		if (str != "") {
			if (previousHeading == "# NAME")
				n = str;
			else if (previousHeading == "# MAJORS")
				addMajor(Major(str));
			else if (previousHeading == "# CONCENTRATIONS")
				addMajor(Major(str));
			else if (previousHeading == "# COURSES")
				addCourse(Course(str));
		}
	}
}

void Student::addMajor(const Major& m) {
	majors.push_back(m);
}

void Student::addMajors(string str) {
	vector<string> record = split(str, ',');
	for (vector<string>::iterator i = record.begin(); i != record.end(); ++i)
		addMajor(*i);
}

void Student::addCourse(const Course& c) {
	courses.push_back(c);
}

void Student::addCourses(string str) {
	vector<string> record = split(str, ',');
	for (vector<string>::iterator i = record.begin(); i != record.end(); ++i)
		addCourse(Course(*i));
}

bool Student::hasTakenCourse(string str) {
	bool userHasTaken = false;
	Course checkAgainst = getCourse(str);
	for (std::vector<Course>::iterator i = courses.begin(); i != courses.end(); ++i)
		if (*i == checkAgainst)
			userHasTaken = true;
	return userHasTaken;
}

ostream& Student::getData(ostream &os) {
	os << name << ", ";
	os << "you are majoring in ";
	for (vector<Major>::iterator i = majors.begin(); i != majors.end(); ++i){
		if (majors.size() == 2) {
			if (i != majors.end()-1)
				os << *i << " and ";
			else
				os << *i << " ";
		}
		else {
			if (i != majors.end()-1)
				os << *i << ", ";
			else
				os << "and " << *i << ", ";
		}
	}
	os << "while taking:" << endl;
	for (vector<Course>::iterator i = courses.begin(); i != courses.end(); ++i)
		os << *i << endl;
	return os;
}

void Student::display() {
	cout << *this << endl;
};

ostream& operator<<(ostream& os, Student& item) {
	os << item.getData(os);
	return os;
}
<commit_msg>Assign name within the constructor<commit_after>#include "data-student.hpp"
using namespace std;

void Student::init(string n, int s, int g, string m) {
	name = n;
	startingYear = s;
	gradutationYear = g;
	addMajors(m);
}

Student::Student() {
	init("", 2000, 2004, "");
}
Student::Student(string n, string s, string e, string m) {
	int startYear = stringToInt(s), endYear = stringToInt(e);
	init(n, startYear, endYear, m);
}

Student::Student(string fn) {
	string yearS, yearE;
	ifstream infile(fn.c_str());
	string nextLine;
	vector<string> lines;

	// load the entire file
	while (infile.peek() != -1) {
		getline(infile, nextLine);
		lines.push_back(nextLine);
	}

	string previousHeading;
	for (vector<string>::iterator i = lines.begin(); i != lines.end(); ++i) {
		string str = *i;
		if (str[0] == '#') {
			previousHeading = *i;
			continue;
		}
		else if (str != "") {
			if (previousHeading == "# NAME")
				name = str;
			else if (previousHeading == "# MAJORS")
				addMajor(Major(str));
			else if (previousHeading == "# CONCENTRATIONS")
				addMajor(Major(str));
			else if (previousHeading == "# COURSES")
				addCourse(Course(str));
		}
	}
}

void Student::addMajor(const Major& m) {
	majors.push_back(m);
}

void Student::addMajors(string str) {
	vector<string> record = split(str, ',');
	for (vector<string>::iterator i = record.begin(); i != record.end(); ++i)
		addMajor(*i);
}

void Student::addCourse(const Course& c) {
	courses.push_back(c);
}

void Student::addCourses(string str) {
	vector<string> record = split(str, ',');
	for (vector<string>::iterator i = record.begin(); i != record.end(); ++i)
		addCourse(Course(*i));
}

bool Student::hasTakenCourse(string str) {
	bool userHasTaken = false;
	Course checkAgainst = getCourse(str);
	for (std::vector<Course>::iterator i = courses.begin(); i != courses.end(); ++i)
		if (*i == checkAgainst)
			userHasTaken = true;
	return userHasTaken;
}

ostream& Student::getData(ostream &os) {
	os << name << ", ";
	os << "you are majoring in ";
	for (vector<Major>::iterator i = majors.begin(); i != majors.end(); ++i){
		if (majors.size() == 2) {
			if (i != majors.end()-1)
				os << *i << " and ";
			else
				os << *i << " ";
		}
		else {
			if (i != majors.end()-1)
				os << *i << ", ";
			else
				os << "and " << *i << ", ";
		}
	}
	os << "while taking:" << endl;
	for (vector<Course>::iterator i = courses.begin(); i != courses.end(); ++i)
		os << *i << endl;
	return os;
}

void Student::display() {
	cout << *this << endl;
};

ostream& operator<<(ostream& os, Student& item) {
	os << item.getData(os);
	return os;
}
<|endoftext|>


<commit_before>/*
 * author: Max Kellermann <mk@cm4all.com>
 */

#include "Client.hxx"
#include "Protocol.hxx"
#include "Builder.hxx"
#include "Parser.hxx"
#include "Prepared.hxx"
#include "mount_list.hxx"
#include "ExitListener.hxx"
#include "system/Error.hxx"
#include "event/Callback.hxx"
#include "util/Error.hxx"
#include "util/Domain.hxx"
#include "util/ScopeExit.hxx"

#include <daemon/log.h>

#include <glib.h>

#include <assert.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>

static constexpr size_t MAX_FDS = 8;

gcc_const
static inline GQuark
spawn_quark(void)
{
    return g_quark_from_static_string("spawn");
}

SpawnServerClient::SpawnServerClient(const SpawnConfig &_config, int _fd)
    :config(_config), fd(_fd),
     read_event(fd, EV_READ|EV_PERSIST,
                MakeSimpleEventCallback(SpawnServerClient, ReadEventCallback),
                this)
{
    read_event.Add();
}

SpawnServerClient::~SpawnServerClient()
{
    if (fd >= 0)
        Close();
}

void
SpawnServerClient::ReplaceSocket(int new_fd)
{
    assert(fd >= 0);
    assert(new_fd >= 0);
    assert(fd != new_fd);

    processes.clear();

    Close();

    fd = new_fd;

    read_event.Set(fd, EV_READ|EV_PERSIST,
                   MakeSimpleEventCallback(SpawnServerClient, ReadEventCallback),
                   this);
    read_event.Add();
}

void
SpawnServerClient::Close()
{
    assert(fd >= 0);

    read_event.Delete();
    close(fd);
    fd = -1;
}

inline void
SpawnServerClient::Send(ConstBuffer<void> payload, ConstBuffer<int> fds)
{
    ::Send<MAX_FDS>(fd, payload, fds);
}

inline void
SpawnServerClient::Send(const SpawnSerializer &s)
{
    ::Send<MAX_FDS>(fd, s);
}

int
SpawnServerClient::Connect()
{
    assert(fd >= 0);

    int sv[2];
    if (socketpair(AF_LOCAL, SOCK_SEQPACKET|SOCK_CLOEXEC|SOCK_NONBLOCK,
                   0, sv) < 0)
        throw MakeErrno("socketpair() failed");

    const int local_fd = sv[0];
    const int remote_fd = sv[1];

    AtScopeExit(remote_fd){ close(remote_fd); };

    static constexpr SpawnRequestCommand cmd = SpawnRequestCommand::CONNECT;

    try {
        Send(ConstBuffer<void>(&cmd, sizeof(cmd)), {&remote_fd, 1});
    } catch (...) {
        close(local_fd);
        std::throw_with_nested(std::runtime_error("Spawn server failed"));
    }

    return local_fd;
}

static void
Serialize(SpawnSerializer &s, const RefenceOptions &_r)
{
    const auto r = _r.Get();
    if (!r.IsNull()) {
        s.Write(SpawnExecCommand::REFENCE);
        s.Write(r.ToVoid());
        s.WriteByte(0);
    }
}

static void
Serialize(SpawnSerializer &s, const NamespaceOptions &ns)
{
    s.WriteOptional(SpawnExecCommand::USER_NS, ns.enable_user);
    s.WriteOptional(SpawnExecCommand::PID_NS, ns.enable_pid);
    s.WriteOptional(SpawnExecCommand::NETWORK_NS, ns.enable_network);
    s.WriteOptional(SpawnExecCommand::IPC_NS, ns.enable_ipc);
    s.WriteOptional(SpawnExecCommand::MOUNT_NS, ns.enable_mount);
    s.WriteOptional(SpawnExecCommand::MOUNT_PROC, ns.mount_proc);
    s.WriteOptionalString(SpawnExecCommand::PIVOT_ROOT, ns.pivot_root);

    if (ns.mount_home != nullptr) {
        s.Write(SpawnExecCommand::MOUNT_HOME);
        s.WriteString(ns.mount_home);
        s.WriteString(ns.home);
    }

    s.WriteOptionalString(SpawnExecCommand::MOUNT_TMP_TMPFS, ns.mount_tmp_tmpfs);
    s.WriteOptionalString(SpawnExecCommand::MOUNT_TMPFS, ns.mount_tmpfs);

    for (auto i = ns.mounts; i != nullptr; i = i->next) {
        s.Write(SpawnExecCommand::BIND_MOUNT);
        s.WriteString(i->source);
        s.WriteString(i->target);
        s.WriteByte(i->writable);
    }

    s.WriteOptionalString(SpawnExecCommand::HOSTNAME, ns.hostname);
}

static void
Serialize(SpawnSerializer &s, unsigned i, const ResourceLimit &rlimit)
{
    if (rlimit.IsEmpty())
        return;

    s.Write(SpawnExecCommand::RLIMIT);
    s.WriteByte(i);

    const struct rlimit &data = rlimit;
    s.WriteT(data);
}

static void
Serialize(SpawnSerializer &s, const ResourceLimits &rlimits)
{
    for (unsigned i = 0; i < RLIM_NLIMITS; ++i)
        Serialize(s, i, rlimits.values[i]);
}

static void
Serialize(SpawnSerializer &s, const UidGid &uid_gid)
{
    if (uid_gid.IsEmpty())
        return;

    s.Write(SpawnExecCommand::UID_GID);
    s.WriteT(uid_gid.uid);
    s.WriteT(uid_gid.gid);

    const size_t n_groups = uid_gid.CountGroups();
    s.WriteByte(n_groups);
    for (size_t i = 0; i < n_groups; ++i)
        s.WriteT(uid_gid.groups[i]);
}

static void
Serialize(SpawnSerializer &s, const PreparedChildProcess &p)
{
    for (const char *i : p.args)
        s.WriteString(SpawnExecCommand::ARG, i);

    for (const char *i : p.env)
        s.WriteString(SpawnExecCommand::SETENV, i);

    s.CheckWriteFd(SpawnExecCommand::STDIN, p.stdin_fd);
    s.CheckWriteFd(SpawnExecCommand::STDOUT, p.stdout_fd);
    s.CheckWriteFd(SpawnExecCommand::STDERR, p.stderr_fd);
    s.CheckWriteFd(SpawnExecCommand::CONTROL, p.control_fd);

    Serialize(s, p.refence);
    Serialize(s, p.ns);
    Serialize(s, p.rlimits);
    Serialize(s, p.uid_gid);

    if (p.no_new_privs)
        s.Write(SpawnExecCommand::NO_NEW_PRIVS);
}

int
SpawnServerClient::SpawnChildProcess(const char *name,
                                     PreparedChildProcess &&p,
                                     ExitListener *listener,
                                     GError **error_r)
{
    /* this check is performed again on the server (which is obviously
       necessary, and the only way to have it secure); this one is
       only here for the developer to see the error earlier in the
       call chain */
    if (!p.uid_gid.IsEmpty() && !config.Verify(p.uid_gid)) {
        g_set_error(error_r, spawn_quark(), 0, "uid/gid not allowed: %d/%d",
                    int(p.uid_gid.uid), int(p.uid_gid.gid));
        return -1;
    }

    const int pid = MakePid();

    SpawnSerializer s(SpawnRequestCommand::EXEC);

    try {
        s.WriteInt(pid);
        s.WriteString(name);

        Serialize(s, p);
    } catch (SpawnPayloadTooLargeError) {
        g_set_error_literal(error_r, spawn_quark(), 0,
                            "Spawn payload is too large");
        return -1;
    }

    try {
        Send(s.GetPayload(), s.GetFds());
    } catch (const std::runtime_error &e) {
        g_set_error(error_r, spawn_quark(), 0,
                    "Spawn server failed: %s", e.what());
        return -1;
    }

    processes.emplace(std::piecewise_construct,
                      std::forward_as_tuple(pid),
                      std::forward_as_tuple(listener));
    return pid;
}

void
SpawnServerClient::SetExitListener(int pid, ExitListener *listener)
{
    auto i = processes.find(pid);
    assert(i != processes.end());

    assert(i->second.listener == nullptr);
    i->second.listener = listener;
}

void
SpawnServerClient::KillChildProcess(int pid, int signo)
{
    auto i = processes.find(pid);
    assert(i != processes.end());
    assert(i->second.listener != nullptr);
    i->second.listener = nullptr;

    SpawnSerializer s(SpawnRequestCommand::KILL);
    s.WriteInt(pid);
    s.WriteInt(signo);

    try {
        Send(s.GetPayload(), s.GetFds());
    } catch (const std::runtime_error &e) {
        daemon_log(1, "failed to send KILL(%d) to spawner: %s\n",
                   pid,e.what());
    }
}

inline void
SpawnServerClient::HandleExitMessage(SpawnPayload payload)
{
    int pid, status;
    payload.ReadInt(pid);
    payload.ReadInt(status);
    if (!payload.IsEmpty())
        throw MalformedSpawnPayloadError();

    auto i = processes.find(pid);
    if (i == processes.end())
        return;

    auto *listener = i->second.listener;
    processes.erase(i);

    if (listener != nullptr)
        listener->OnChildProcessExit(status);
}

inline void
SpawnServerClient::HandleMessage(ConstBuffer<uint8_t> payload)
{
    const auto cmd = (SpawnResponseCommand)payload.shift();

    switch (cmd) {
    case SpawnResponseCommand::EXIT:
        HandleExitMessage(SpawnPayload(payload));
        break;
    }
}

inline void
SpawnServerClient::ReadEventCallback()
{
    uint8_t payload[8192];

    struct iovec iov;
    iov.iov_base = payload;
    iov.iov_len = sizeof(payload);

    struct msghdr msg = {
        .msg_name = nullptr,
        .msg_namelen = 0,
        .msg_iov = &iov,
        .msg_iovlen = 1,
        .msg_control = nullptr,
        .msg_controllen = 0,
    };

    ssize_t nbytes = recvmsg(fd, &msg, MSG_DONTWAIT|MSG_CMSG_CLOEXEC);
    if (nbytes <= 0) {
        if (nbytes < 0)
            daemon_log(2, "recvmsg() from spawner failed: %s\n",
                       strerror(errno));
        else
            daemon_log(2, "spawner closed the socket\n");
        Close();
        return;
    }

    HandleMessage({payload, size_t(nbytes)});
}
<commit_msg>spawn/Client: erase object in KillChildProcess()<commit_after>/*
 * author: Max Kellermann <mk@cm4all.com>
 */

#include "Client.hxx"
#include "Protocol.hxx"
#include "Builder.hxx"
#include "Parser.hxx"
#include "Prepared.hxx"
#include "mount_list.hxx"
#include "ExitListener.hxx"
#include "system/Error.hxx"
#include "event/Callback.hxx"
#include "util/Error.hxx"
#include "util/Domain.hxx"
#include "util/ScopeExit.hxx"

#include <daemon/log.h>

#include <glib.h>

#include <assert.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>

static constexpr size_t MAX_FDS = 8;

gcc_const
static inline GQuark
spawn_quark(void)
{
    return g_quark_from_static_string("spawn");
}

SpawnServerClient::SpawnServerClient(const SpawnConfig &_config, int _fd)
    :config(_config), fd(_fd),
     read_event(fd, EV_READ|EV_PERSIST,
                MakeSimpleEventCallback(SpawnServerClient, ReadEventCallback),
                this)
{
    read_event.Add();
}

SpawnServerClient::~SpawnServerClient()
{
    if (fd >= 0)
        Close();
}

void
SpawnServerClient::ReplaceSocket(int new_fd)
{
    assert(fd >= 0);
    assert(new_fd >= 0);
    assert(fd != new_fd);

    processes.clear();

    Close();

    fd = new_fd;

    read_event.Set(fd, EV_READ|EV_PERSIST,
                   MakeSimpleEventCallback(SpawnServerClient, ReadEventCallback),
                   this);
    read_event.Add();
}

void
SpawnServerClient::Close()
{
    assert(fd >= 0);

    read_event.Delete();
    close(fd);
    fd = -1;
}

inline void
SpawnServerClient::Send(ConstBuffer<void> payload, ConstBuffer<int> fds)
{
    ::Send<MAX_FDS>(fd, payload, fds);
}

inline void
SpawnServerClient::Send(const SpawnSerializer &s)
{
    ::Send<MAX_FDS>(fd, s);
}

int
SpawnServerClient::Connect()
{
    assert(fd >= 0);

    int sv[2];
    if (socketpair(AF_LOCAL, SOCK_SEQPACKET|SOCK_CLOEXEC|SOCK_NONBLOCK,
                   0, sv) < 0)
        throw MakeErrno("socketpair() failed");

    const int local_fd = sv[0];
    const int remote_fd = sv[1];

    AtScopeExit(remote_fd){ close(remote_fd); };

    static constexpr SpawnRequestCommand cmd = SpawnRequestCommand::CONNECT;

    try {
        Send(ConstBuffer<void>(&cmd, sizeof(cmd)), {&remote_fd, 1});
    } catch (...) {
        close(local_fd);
        std::throw_with_nested(std::runtime_error("Spawn server failed"));
    }

    return local_fd;
}

static void
Serialize(SpawnSerializer &s, const RefenceOptions &_r)
{
    const auto r = _r.Get();
    if (!r.IsNull()) {
        s.Write(SpawnExecCommand::REFENCE);
        s.Write(r.ToVoid());
        s.WriteByte(0);
    }
}

static void
Serialize(SpawnSerializer &s, const NamespaceOptions &ns)
{
    s.WriteOptional(SpawnExecCommand::USER_NS, ns.enable_user);
    s.WriteOptional(SpawnExecCommand::PID_NS, ns.enable_pid);
    s.WriteOptional(SpawnExecCommand::NETWORK_NS, ns.enable_network);
    s.WriteOptional(SpawnExecCommand::IPC_NS, ns.enable_ipc);
    s.WriteOptional(SpawnExecCommand::MOUNT_NS, ns.enable_mount);
    s.WriteOptional(SpawnExecCommand::MOUNT_PROC, ns.mount_proc);
    s.WriteOptionalString(SpawnExecCommand::PIVOT_ROOT, ns.pivot_root);

    if (ns.mount_home != nullptr) {
        s.Write(SpawnExecCommand::MOUNT_HOME);
        s.WriteString(ns.mount_home);
        s.WriteString(ns.home);
    }

    s.WriteOptionalString(SpawnExecCommand::MOUNT_TMP_TMPFS, ns.mount_tmp_tmpfs);
    s.WriteOptionalString(SpawnExecCommand::MOUNT_TMPFS, ns.mount_tmpfs);

    for (auto i = ns.mounts; i != nullptr; i = i->next) {
        s.Write(SpawnExecCommand::BIND_MOUNT);
        s.WriteString(i->source);
        s.WriteString(i->target);
        s.WriteByte(i->writable);
    }

    s.WriteOptionalString(SpawnExecCommand::HOSTNAME, ns.hostname);
}

static void
Serialize(SpawnSerializer &s, unsigned i, const ResourceLimit &rlimit)
{
    if (rlimit.IsEmpty())
        return;

    s.Write(SpawnExecCommand::RLIMIT);
    s.WriteByte(i);

    const struct rlimit &data = rlimit;
    s.WriteT(data);
}

static void
Serialize(SpawnSerializer &s, const ResourceLimits &rlimits)
{
    for (unsigned i = 0; i < RLIM_NLIMITS; ++i)
        Serialize(s, i, rlimits.values[i]);
}

static void
Serialize(SpawnSerializer &s, const UidGid &uid_gid)
{
    if (uid_gid.IsEmpty())
        return;

    s.Write(SpawnExecCommand::UID_GID);
    s.WriteT(uid_gid.uid);
    s.WriteT(uid_gid.gid);

    const size_t n_groups = uid_gid.CountGroups();
    s.WriteByte(n_groups);
    for (size_t i = 0; i < n_groups; ++i)
        s.WriteT(uid_gid.groups[i]);
}

static void
Serialize(SpawnSerializer &s, const PreparedChildProcess &p)
{
    for (const char *i : p.args)
        s.WriteString(SpawnExecCommand::ARG, i);

    for (const char *i : p.env)
        s.WriteString(SpawnExecCommand::SETENV, i);

    s.CheckWriteFd(SpawnExecCommand::STDIN, p.stdin_fd);
    s.CheckWriteFd(SpawnExecCommand::STDOUT, p.stdout_fd);
    s.CheckWriteFd(SpawnExecCommand::STDERR, p.stderr_fd);
    s.CheckWriteFd(SpawnExecCommand::CONTROL, p.control_fd);

    Serialize(s, p.refence);
    Serialize(s, p.ns);
    Serialize(s, p.rlimits);
    Serialize(s, p.uid_gid);

    if (p.no_new_privs)
        s.Write(SpawnExecCommand::NO_NEW_PRIVS);
}

int
SpawnServerClient::SpawnChildProcess(const char *name,
                                     PreparedChildProcess &&p,
                                     ExitListener *listener,
                                     GError **error_r)
{
    /* this check is performed again on the server (which is obviously
       necessary, and the only way to have it secure); this one is
       only here for the developer to see the error earlier in the
       call chain */
    if (!p.uid_gid.IsEmpty() && !config.Verify(p.uid_gid)) {
        g_set_error(error_r, spawn_quark(), 0, "uid/gid not allowed: %d/%d",
                    int(p.uid_gid.uid), int(p.uid_gid.gid));
        return -1;
    }

    const int pid = MakePid();

    SpawnSerializer s(SpawnRequestCommand::EXEC);

    try {
        s.WriteInt(pid);
        s.WriteString(name);

        Serialize(s, p);
    } catch (SpawnPayloadTooLargeError) {
        g_set_error_literal(error_r, spawn_quark(), 0,
                            "Spawn payload is too large");
        return -1;
    }

    try {
        Send(s.GetPayload(), s.GetFds());
    } catch (const std::runtime_error &e) {
        g_set_error(error_r, spawn_quark(), 0,
                    "Spawn server failed: %s", e.what());
        return -1;
    }

    processes.emplace(std::piecewise_construct,
                      std::forward_as_tuple(pid),
                      std::forward_as_tuple(listener));
    return pid;
}

void
SpawnServerClient::SetExitListener(int pid, ExitListener *listener)
{
    auto i = processes.find(pid);
    assert(i != processes.end());

    assert(i->second.listener == nullptr);
    i->second.listener = listener;
}

void
SpawnServerClient::KillChildProcess(int pid, int signo)
{
    auto i = processes.find(pid);
    assert(i != processes.end());
    assert(i->second.listener != nullptr);
    processes.erase(i);

    SpawnSerializer s(SpawnRequestCommand::KILL);
    s.WriteInt(pid);
    s.WriteInt(signo);

    try {
        Send(s.GetPayload(), s.GetFds());
    } catch (const std::runtime_error &e) {
        daemon_log(1, "failed to send KILL(%d) to spawner: %s\n",
                   pid,e.what());
    }
}

inline void
SpawnServerClient::HandleExitMessage(SpawnPayload payload)
{
    int pid, status;
    payload.ReadInt(pid);
    payload.ReadInt(status);
    if (!payload.IsEmpty())
        throw MalformedSpawnPayloadError();

    auto i = processes.find(pid);
    if (i == processes.end())
        return;

    auto *listener = i->second.listener;
    processes.erase(i);

    if (listener != nullptr)
        listener->OnChildProcessExit(status);
}

inline void
SpawnServerClient::HandleMessage(ConstBuffer<uint8_t> payload)
{
    const auto cmd = (SpawnResponseCommand)payload.shift();

    switch (cmd) {
    case SpawnResponseCommand::EXIT:
        HandleExitMessage(SpawnPayload(payload));
        break;
    }
}

inline void
SpawnServerClient::ReadEventCallback()
{
    uint8_t payload[8192];

    struct iovec iov;
    iov.iov_base = payload;
    iov.iov_len = sizeof(payload);

    struct msghdr msg = {
        .msg_name = nullptr,
        .msg_namelen = 0,
        .msg_iov = &iov,
        .msg_iovlen = 1,
        .msg_control = nullptr,
        .msg_controllen = 0,
    };

    ssize_t nbytes = recvmsg(fd, &msg, MSG_DONTWAIT|MSG_CMSG_CLOEXEC);
    if (nbytes <= 0) {
        if (nbytes < 0)
            daemon_log(2, "recvmsg() from spawner failed: %s\n",
                       strerror(errno));
        else
            daemon_log(2, "spawner closed the socket\n");
        Close();
        return;
    }

    HandleMessage({payload, size_t(nbytes)});
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2010, 2011, Gostai S.A.S.
 *
 * This software is provided "as is" without warranty of any kind,
 * either expressed or implied, including but not limited to the
 * implied warranties of fitness for a particular purpose.
 *
 * See the LICENSE file for more information.
 */

#include <libport/bind.hh>
#include <libport/hash.hh>
#include <libport/symbol.hh>
#include <libport/test.hh>

using boost::bind;
using libport::test_suite;

template <typename T>
void
test_hash_map()
{
  boost::unordered_map<T, std::string> map;

  BOOST_CHECK(map.empty());
  BOOST_CHECK_EQUAL(map.size(), 0u);

  BOOST_CHECK_NO_THROW(map[T("foo")] = "foo");
  BOOST_CHECK(!map.empty());
  BOOST_CHECK_EQUAL(map.size(), 1u);
  BOOST_CHECK_EQUAL(map[T("foo")], "foo");

  BOOST_CHECK_NO_THROW(map[T("bar")] = "bar");
  BOOST_CHECK(!map.empty());
  BOOST_CHECK_EQUAL(map.size(), 2u);
  BOOST_CHECK_EQUAL(map[T("foo")], "foo");
  BOOST_CHECK_EQUAL(map[T("bar")], "bar");

  BOOST_CHECK_NO_THROW(map[T("bar")] = "baz");
  BOOST_CHECK(!map.empty());
  BOOST_CHECK_EQUAL(map.size(), 2u);
  BOOST_CHECK_EQUAL(map[T("foo")], "foo");
  BOOST_CHECK_EQUAL(map[T("bar")], "baz");

  BOOST_CHECK_NO_THROW(map.erase(T("foo")));
  BOOST_CHECK(!map.empty());
  BOOST_CHECK_EQUAL(map.size(), 1u);
  BOOST_CHECK_EQUAL(map[T("bar")], "baz");

  BOOST_CHECK_NO_THROW(map.erase(T("bar")));
  BOOST_CHECK(map.empty());
  BOOST_CHECK_EQUAL(map.size(), 0u);
}

test_suite*
init_test_suite()
{
  test_suite* suite = BOOST_TEST_SUITE("Libport.Hash");

# define def(Type)                                            \
  suite->add(BOOST_TEST_CASE(&test_hash_map<Type>))

  def(const char*);
  def(char*);
  def(std::string);
  def(libport::Symbol);
# undef def
  return suite;
}
<commit_msg>msvc: fix Libport.Hash failures.<commit_after>/*
 * Copyright (C) 2010, 2011, Gostai S.A.S.
 *
 * This software is provided "as is" without warranty of any kind,
 * either expressed or implied, including but not limited to the
 * implied warranties of fitness for a particular purpose.
 *
 * See the LICENSE file for more information.
 */

#include <libport/bind.hh>
#include <libport/foreach.hh>
#include <libport/hash.hh>
#include <libport/symbol.hh>
#include <libport/test.hh>

using boost::bind;
using libport::test_suite;

template <typename K, typename V>
std::ostream&
operator<<(std::ostream& o, const boost::unordered_map<K, V>& m)
{
  typedef boost::unordered_map<K, V> map_type;
  typedef map_type::value_type       value_type;
  foreach (const value_type& p, m)
    o << p.first << " -> " << p.second << std::endl;
  return o;
}

template <typename T>
void
test_hash_map()
{
  boost::unordered_map<T, std::string> map;

  BOOST_CHECK(map.empty());
  BOOST_CHECK_EQUAL(map.size(), 0u);

  BOOST_CHECK_NO_THROW(map[T("foo")] = "foo");
  BOOST_TEST_MESSAGE(map);
  BOOST_CHECK(!map.empty());
  BOOST_CHECK_EQUAL(map.size(), 1u);
  BOOST_CHECK_EQUAL(map[T("foo")], "foo");
  BOOST_TEST_MESSAGE(map);

  BOOST_CHECK_NO_THROW(map[T("bar")] = "bar");
  BOOST_TEST_MESSAGE(map);
  BOOST_CHECK(!map.empty());
  BOOST_CHECK_EQUAL(map.size(), 2u);
  BOOST_CHECK_EQUAL(map[T("foo")], "foo");
  BOOST_CHECK_EQUAL(map[T("bar")], "bar");

  BOOST_CHECK_NO_THROW(map[T("bar")] = "baz");
  BOOST_CHECK(!map.empty());
  BOOST_CHECK_EQUAL(map.size(), 2u);
  BOOST_CHECK_EQUAL(map[T("foo")], "foo");
  BOOST_CHECK_EQUAL(map[T("bar")], "baz");
  BOOST_TEST_MESSAGE(map);

  BOOST_CHECK_NO_THROW(map.erase(T("foo")));
  BOOST_TEST_MESSAGE(map);
  BOOST_CHECK(!map.empty());
  BOOST_CHECK_EQUAL(map.size(), 1u);
  BOOST_CHECK_EQUAL(map[T("bar")], "baz");

  BOOST_CHECK_NO_THROW(map.erase(T("bar")));
  BOOST_TEST_MESSAGE(map);
  BOOST_CHECK(map.empty());
  BOOST_CHECK_EQUAL(map.size(), 0u);
}

test_suite*
init_test_suite()
{
  test_suite* suite = BOOST_TEST_SUITE("Libport.Hash");

# define def(Type)                                            \
  suite->add(BOOST_TEST_CASE(&test_hash_map<Type>))

  def(std::string);
  def(libport::Symbol);
  // Don't check on char*.
  //
  // There is no hash function for char*.  Since the test is using
  // const strings, GCC is certainly keeping a single copy of them
  // all, so since "foo" == "foo", of course, the hash table works,
  // but that's merely "chance".  It turns out that Visual does not
  // factor the strings (well, that's my understanding), so "of
  // course" the hash "does not work": it hashes on the addresses, not
  // the contents.  Since we don't use hashes on char*, let's not
  // force an interpretation at the library level (after all, we might
  // want to hash char* like any T*), just don't test char*.
# undef def
  return suite;
}
<|endoftext|>