<commit_before>/**
 * elf-loader.cpp: loader of elf files to guest memory
 * @author Pavel Kryukov
 * Copyright 2018 uArchSim iLab project
 */

#include "elf_loader.h"

#include <elfio/elfio.hpp>
#include <memory/memory.h>

#include <string>

struct InvalidElfSection : Exception
{
    explicit InvalidElfSection(const std::string& section_name) :
        Exception("Malformed ELF section", section_name) { }
};

static void load_elf_section( WriteableMemory* memory, const ELFIO::section& section, AddrDiff offset)
{
    using namespace std::literals::string_literals;
    if ( section.get_address() == 0 || section.get_data() == nullptr)
        throw InvalidElfSection( "\""s + section.get_name() + "\""s);

    memory->memcpy_host_to_guest( section.get_address() + offset, byte_cast( section.get_data()), section.get_size());
}

ElfLoader::ElfLoader( const std::string& filename, AddrDiff offset)
    : reader( std::make_unique<ELFIO::elfio>())
    , offset( offset)
{
    if ( !reader->load( filename))
        throw InvalidElfFile( filename);
}

void ElfLoader::load_to( WriteableMemory *memory) const
{
    for ( const auto& section : reader->sections)
        if ( ( section->get_flags() & SHF_ALLOC) != 0)
            load_elf_section( memory, *section, offset);
}

Addr ElfLoader::get_startPC() const
{
    using namespace ELFIO;
    for ( const auto& section : reader->sections) {
        if ( section->get_type() != SHT_SYMTAB)
            continue;

        symbol_section_accessor symbols(*reader, section);
        for ( unsigned int j = 0; j < symbols.get_symbols_num(); ++j ) {
            std::string name;
            Elf64_Addr value = 0;
            Elf_Xword size;
            unsigned char bind;
            unsigned char type;
            Elf_Half section_index;
            unsigned char other;
            symbols.get_symbol( j, name, value, size, bind, type, section_index, other );
            if ( name == "_start")
                return offset + value;
        }
    }

    if ( reader->sections[ ".text"] != nullptr) {
        std::cout << "Warning: no _start label found, defaulting to '.text' section\n";
        return offset + reader->sections[ ".text"]->get_address();
    }

    std::cout << "Warning: no entry point found, returning 0x0\n";
    return offset;
}

ElfLoader::~ElfLoader() = default;
<commit_msg>Fix Clang-Tidy warnings (#981)<commit_after>/**
 * elf-loader.cpp: loader of elf files to guest memory
 * @author Pavel Kryukov
 * Copyright 2018 uArchSim iLab project
 */

#include "elf_loader.h"

#include <elfio/elfio.hpp>
#include <memory/memory.h>

#include <string>

struct InvalidElfSection : Exception
{
    explicit InvalidElfSection(const std::string& section_name) :
        Exception("Malformed ELF section", section_name) { }
};

static void load_elf_section( WriteableMemory* memory, const ELFIO::section& section, AddrDiff offset)
{
    using namespace std::literals::string_literals;
    if ( section.get_address() == 0 || section.get_data() == nullptr)
        throw InvalidElfSection( "\""s + section.get_name() + "\""s);

    memory->memcpy_host_to_guest( section.get_address() + offset, byte_cast( section.get_data()), section.get_size());
}

ElfLoader::ElfLoader( const std::string& filename, AddrDiff offset)
    : reader( std::make_unique<ELFIO::elfio>())
    , offset( offset)
{
    if ( !reader->load( filename))
        throw InvalidElfFile( filename);
}

void ElfLoader::load_to( WriteableMemory *memory) const
{
    for ( const auto& section : reader->sections)
        if ( ( section->get_flags() & SHF_ALLOC) != 0)
            load_elf_section( memory, *section, offset);
}

Addr ElfLoader::get_startPC() const
{
    for ( const auto& section : reader->sections) {
        if ( section->get_type() != SHT_SYMTAB)
            continue;

        ELFIO::symbol_section_accessor symbols(*reader, section);
        for ( ELFIO::Elf_Xword j = 0; j < symbols.get_symbols_num(); ++j ) {
            std::string name;
            ELFIO::Elf64_Addr value = 0;
            ELFIO::Elf_Xword size;
            unsigned char bind;
            unsigned char type;
            ELFIO::Elf_Half section_index;
            unsigned char other;
            symbols.get_symbol( j, name, value, size, bind, type, section_index, other );
            if ( name == "_start")
                return offset + value;
        }
    }

    if ( reader->sections[ ".text"] != nullptr) {
        std::cout << "Warning: no _start label found, defaulting to '.text' section\n";
        return offset + reader->sections[ ".text"]->get_address();
    }

    std::cout << "Warning: no entry point found, returning 0x0\n";
    return offset;
}

ElfLoader::~ElfLoader() = default;
<|endoftext|>


<commit_before>// \file standard.cpp
// Implementation of some standard lisp operations
//
#include "yacas/yacasprivate.h"
#include "yacas/standard.h"
#include "yacas/lispatom.h"
#include "yacas/lisperror.h"
#include "yacas/lispenvironment.h"

#include "yacas/lispio.h"
#include "yacas/platfileio.h"
#include "yacas/lispenvironment.h"
#include "yacas/tokenizer.h"
#include "yacas/infixparser.h"
#include "yacas/lispeval.h"
#include "yacas/stringio.h"
#include "yacas/numbers.h"

bool InternalIsList(const LispPtr& aPtr)
{
    if (!aPtr)
        return false;
    if (!aPtr->SubList())
        return false;
    if (!(*aPtr->SubList()))
        return false;
    // The following happens with IsList(UnList({foo(x), a, b}))
    if ((*aPtr->SubList())->String()->c_str() == nullptr)
        return false;
    //TODO this StrEqual is far from perfect. We could pass in a LispEnvironment object...
    if (std::strcmp((*aPtr->SubList())->String()->c_str(), "List"))
        return false;
    return true;
}

bool InternalIsString(LispString * aOriginal)
{
    if (aOriginal)
        if ((*aOriginal)[0] == '\"')
            if ((*aOriginal)[aOriginal->size()-1] == '\"')
                return true;
    return false;
}

/* TODO: in documenting the choices made in the C++ engine, perhaps document why I pass objects that should contain the
   result by reference. Result string passed in by reference to avoid copy-constructors etcetera (allowing
   the code to share the same LispString in different places).
 */
void InternalUnstringify(LispString& aResult, const LispString * aOriginal)
{
  /*TODO: should these not be checked also higher up, and should this not be an assert at this level?
   * ideally this function should be as efficient as possible (allowing for a code generator to generate
   * compiled code that calls this function immediately. The compiler could prove that the input is valid,
   * so the checks would not be needed here in a non-debug run).
   *
   * Also do not forget to make the change in the Java version then, and find the other places where this is relevant.
   */
    if (!aOriginal || (*aOriginal)[0] != '\"')
        throw LispErrInvalidArg();

  LispInt nrc = aOriginal->size()-1;

  if (!aOriginal || (*aOriginal)[nrc] != '\"')
      throw LispErrInvalidArg();

  aResult.resize(nrc);
  for (LispInt i = 1; i < nrc; i++)
    aResult[i-1] = (*aOriginal)[i];
}

void InternalStringify(LispString& aResult, const LispString* aOriginal)
{
    if (!aOriginal)
        throw LispErrInvalidArg();

    LispInt nrc=aOriginal->size()-1;
    aResult.resize(nrc+3);
    LispInt i;
    aResult[0] = '\"';
    for (i=0;i<nrc;i++)
        aResult[i+1] = (*aOriginal)[i];
    aResult[nrc+1] = '\"';
    aResult[nrc+2] = '\0';
}

void InternalIntToAscii(LispChar * aTrg,LispInt aInt)
{
    LispInt ind=0;
    if (aInt < 0)
    {
        *aTrg++ = '-';
        aInt = -aInt;
    }
    while (aInt != 0)
    {
        aTrg[ind] = '0'+(aInt%10);
        ind++;
        aInt/=10;
    }

    //zero
    if (ind == 0)
    {
        aTrg[0] = '0';
        ind++;
    }
    aTrg[ind]='\0';
    LispInt i;
    for (i=0;i<(ind>>1);i++)
    {
        LispChar swap=aTrg[i];
        aTrg[i] = aTrg[ind-i-1];
        aTrg[ind-i-1] = swap;
    }
}

LispInt InternalAsciiToInt(const LispString& aString)
{
    const LispChar* ptr = aString.c_str();

    if (!IsNumber(ptr, false))
        throw LispErrInvalidArg();

    return std::atoi(ptr);
}

bool IsNumber(const LispChar * ptr,bool aAllowFloat)
{
    if (*ptr == '-' || *ptr == '+')
        ptr++;

    LispInt nrDigits=0;
    LispInt index=0;
    while(ptr[index] >= '0' && ptr[index] <= '9')
    {
        nrDigits++;
        index++;
    }
    if (ptr[index] == '.')
    {
        if (!aAllowFloat)
            return false;
        index++;
        while(ptr[index] >= '0' && ptr[index] <= '9')
        {
            nrDigits++;
            index++;
        }
    }
    if (nrDigits == 0)
        return false;
    if (ptr[index] == 'e' || ptr[index] == 'E')
    {
        if (!aAllowFloat)
            return false;
        if (!NumericSupportForMantissa())
            return false;
        index++;
        if (ptr[index] == '-' || ptr[index] == '+') index++;
        while(ptr[index] >= '0' && ptr[index] <= '9') index++;
    }
    if (ptr[index] != '\0') return false;
    return true;
}

void InternalNth(LispPtr& aResult, const LispPtr& aArg, LispInt n)
{
    if (n < 0 || !aArg || !aArg->SubList())
        throw LispErrInvalidArg();

    LispIterator iter(*aArg->SubList());

    while (n>0)
    {
        if (!iter.getObj())
            throw LispErrInvalidArg();

        ++iter;
        n--;
    }

    if (!iter.getObj())
        throw LispErrInvalidArg();

    aResult = (iter.getObj()->Copy());
}

void InternalTail(LispPtr& aResult, const LispPtr& aArg)
{
    if (!aArg)
        throw LispErrInvalidArg();

    LispPtr* iter = aArg->SubList();

    if (!iter || !*iter)
        throw LispErrInvalidArg();

    aResult = (LispSubList::New((*iter)->Nixed()));
}

void InternalReverseList(LispPtr& aResult, const LispPtr& aOriginal)
{
    LispPtr iter(aOriginal);
    LispPtr previous;
    LispPtr tail(aOriginal);

    while (!!iter)
    {
        tail = iter->Nixed();
        iter->Nixed() = (previous);
        previous = iter;
        iter = tail;
    }
    aResult = previous;
}

void InternalFlatCopy(LispPtr& aResult, const LispPtr& aOriginal)
{
    LispConstIterator orig(aOriginal);
    LispIterator res(aResult);

    while (orig.getObj())
    {
        (*res) = (orig.getObj()->Copy());
        ++orig;
        ++res;
    }
}

LispInt InternalListLength(const LispPtr& aOriginal)
{
    LispConstIterator iter(aOriginal);
    LispInt length = 0;
    while (iter.getObj())
    {
        /*
         if (iter()->String())
            printf("%s ",iter()->String()->c_str());
         */
        ++iter;
        length++;
    }
    return length;
}

bool InternalEquals(LispEnvironment& aEnvironment,
                    const LispPtr& aExpression1,
                    const LispPtr& aExpression2)
{
    // Handle pointers to same, or nullptr
    if (aExpression1.ptr() == aExpression2.ptr())  // compare pointers to LispObject
    {
        return true;
    }

/*TODO This code would be better, if BigNumber::Equals works*/

    BigNumber *n1 = aExpression1->Number(aEnvironment.Precision());
    BigNumber *n2 = aExpression2->Number(aEnvironment.Precision());
    if (!(!n1 && !n2) )
    {
        if (n1 == n2)
        {
            return true;
        }
        if (!n1) return false;
        if (!n2) return false;
        if (n1->Equals(*n2)) return true;
//this should be enabled
        return false;
    }

    //Pointers to strings should be the same
    if (aExpression1->String() != aExpression2->String())
    {
        return false;
    }

    // Handle same sublists, or nullptr
    if (aExpression1->SubList() == aExpression2->SubList())
    {
        return true;
    }

    // Now check the sublists
    if (aExpression1->SubList())
    {
        if (!aExpression2->SubList())
        {
            return false;
        }
        LispIterator iter1(*aExpression1->SubList());
        LispIterator iter2(*aExpression2->SubList());

        while (iter1.getObj() && iter2.getObj())
        {
            // compare two list elements
            if (!InternalEquals(aEnvironment, *iter1, *iter2))
            {
                return false;
            }

            // Step to next
            ++iter1;
            ++iter2;
        }
        // Lists don't have the same length
        if (iter1.getObj() != iter2.getObj())
            return false;

        // Same!
        return true;
    }

    // expressions sublists are not the same!
    return false;
}

void DoInternalLoad(LispEnvironment& aEnvironment,LispInput* aInput)
{
    LispLocalInput localInput(aEnvironment, aInput);

    // TODO make "EndOfFile" a global thing
    // read-parse-eval to the end of file
    LispString * eof = aEnvironment.HashTable().LookUp("EndOfFile");
    bool endoffile = false;

    LispTokenizer tok;
    InfixParser parser(tok,
                       *aEnvironment.CurrentInput(),
                       aEnvironment,
                       aEnvironment.PreFix(),
                       aEnvironment.InFix(),
                       aEnvironment.PostFix(),
                       aEnvironment.Bodied());

    while (!endoffile)
    {
        LispPtr readIn;
        // Read expression
        parser.Parse(readIn);

        if (!readIn)
            throw LispErrReadingFile();

        // Check for end of file
        if (readIn->String() == eof)
        {
            endoffile = true;
            }
        // Else evaluate
        else
        {
            LispPtr result;
            aEnvironment.iEvaluator->Eval(aEnvironment, result, readIn);
        }
    }
}

void InternalLoad(LispEnvironment& aEnvironment, const LispString* aFileName)
{
    LispString oper;
    InternalUnstringify(oper, aFileName);

    LispString * contents = aEnvironment.FindCachedFile(oper.c_str());
    LispString * hashedname = aEnvironment.HashTable().LookUp(oper.c_str());

    InputStatus oldstatus = aEnvironment.iInputStatus;
    aEnvironment.iInputStatus.SetTo(hashedname->c_str());

    if (contents)
    {
        StringInput newInput(*contents,aEnvironment.iInputStatus);
        DoInternalLoad(aEnvironment,&newInput);
        delete contents;
    }
    else
    {
        //TODO make the file api platform independent!!!!
        // Open file
        LispLocalFile localFP(aEnvironment, hashedname->c_str(),true,
                              aEnvironment.iInputDirectories);

        if (!localFP.stream.is_open())
            throw LispErrFileNotFound();

        CachedStdFileInput newInput(localFP,aEnvironment.iInputStatus);
        DoInternalLoad(aEnvironment,&newInput);
    }
    aEnvironment.iInputStatus.RestoreFrom(oldstatus);
}

void InternalUse(LispEnvironment& aEnvironment,LispString * aFileName)
{
    LispDefFile* def = aEnvironment.DefFiles().File(aFileName);
    if (!def->IsLoaded())
    {
        def->SetLoaded();
        InternalLoad(aEnvironment,aFileName);
    }
}

void InternalApplyString(LispEnvironment& aEnvironment, LispPtr& aResult,
                 LispString * aOperator,LispPtr& aArgs)
{
    if (!InternalIsString(aOperator))
        throw LispErrNotString();

    LispObject *head =
        LispAtom::New(aEnvironment,SymbolName(aEnvironment, aOperator->c_str())->c_str());
    head->Nixed() = (aArgs);
    LispPtr body(LispSubList::New(head));
    aEnvironment.iEvaluator->Eval(aEnvironment, aResult, body);
}

void InternalApplyPure(LispPtr& oper,LispPtr& args2,LispPtr& aResult,LispEnvironment& aEnvironment)
{
    LispPtr * chk1 = oper->SubList();

    if (!chk1)
        throw LispErrInvalidArg();

    LispPtr oper2((*chk1)->Nixed());

    if (!oper2)
        throw LispErrInvalidArg();

    LispPtr body(oper2->Nixed());

    if (!body)
        throw LispErrInvalidArg();

    LispPtr * chk2 = oper2->SubList();

    if (!chk2 || !*chk2)
        throw LispErrInvalidArg();

    oper2 = ((*chk2)->Nixed());

    LispLocalFrame frame(aEnvironment,false);

    while (!!oper2)  {
        if (!args2)
            throw LispErrInvalidArg();

        LispString* var = oper2->String();

        if (!var)
            throw LispErrInvalidArg();

        LispPtr newly(args2->Copy());

        aEnvironment.NewLocal(var,newly);

        oper2 = (oper2->Nixed());

        args2 = (args2->Nixed());
    }

    if (args2)
        throw LispErrInvalidArg();

    aEnvironment.iEvaluator->Eval(aEnvironment, aResult, body);
}

void InternalEvalString(LispEnvironment& aEnvironment, LispPtr& aResult,
                        const LispChar* aString)
{
    LispString full(aString);
    full.push_back(';');
    StringInput input(full,aEnvironment.iInputStatus);
    LispPtr lispexpr;
    LispTokenizer &tok = *aEnvironment.iCurrentTokenizer;
    InfixParser parser(tok, input,
                       aEnvironment,
                       aEnvironment.PreFix(),
                       aEnvironment.InFix(),
                       aEnvironment.PostFix(),
                       aEnvironment.Bodied());
    parser.Parse(lispexpr);

    aEnvironment.iEvaluator->Eval(aEnvironment, aResult, lispexpr);
}

LispObject* operator+(const LispObjectAdder& left, const LispObjectAdder& right)
{
  LispObject* trav = left.iPtr;
  while (!!trav->Nixed())
  {
        trav = trav->Nixed();
  }
  trav->Nixed() = (right.iPtr);
  return left.iPtr;
}

void ParseExpression(LispPtr& aResult, const LispChar* aString,LispEnvironment& aEnvironment)
{
    LispString full(aString);
    full.push_back(';');
    StringInput input(full,aEnvironment.iInputStatus);
    aEnvironment.iInputStatus.SetTo("String");
    LispTokenizer &tok = *aEnvironment.iCurrentTokenizer;
    InfixParser parser(tok,
                       input,
                       aEnvironment,
                       aEnvironment.PreFix(),
                       aEnvironment.InFix(),
                       aEnvironment.PostFix(),
                       aEnvironment.Bodied());
    parser.Parse(aResult);
}

void ReturnUnEvaluated(LispPtr& aResult,LispPtr& aArguments,
                       LispEnvironment& aEnvironment)
{
    LispPtr full(aArguments->Copy());
    aResult = (LispSubList::New(full));

    LispIterator iter(aArguments);
    ++iter;

    while (iter.getObj())
    {
        LispPtr next;
        aEnvironment.iEvaluator->Eval(aEnvironment, next, *iter);
        full->Nixed() = (next);
        full = (next);
        ++iter;
    }
    full->Nixed() = (nullptr);
}

void PrintExpression(LispString& aResult,
                     LispPtr& aExpression,
                     LispEnvironment& aEnvironment,
                     std::size_t aMaxChars)
{
    aResult.clear();
    StringOutput newOutput(aResult);
    InfixPrinter infixprinter(aEnvironment.PreFix(),
                              aEnvironment.InFix(),
                              aEnvironment.PostFix(),
                              aEnvironment.Bodied());
    infixprinter.Print(aExpression, newOutput, aEnvironment);
    if (aMaxChars > 0 && aResult.size()>aMaxChars)
    {
        aResult.resize(aMaxChars - 3);
        aResult += "...";
    }
}

LispString* SymbolName(LispEnvironment& aEnvironment,
                       const LispChar * aSymbol)
{
    if (aSymbol[0] == '\"')
        return aEnvironment.HashTable().LookUpCounted(aSymbol + 1, std::strlen(aSymbol) - 2);
    else
        return aEnvironment.HashTable().LookUp(aSymbol);
}

<commit_msg>adjustments to correct string semantics<commit_after>// \file standard.cpp
// Implementation of some standard lisp operations
//
#include "yacas/yacasprivate.h"
#include "yacas/standard.h"
#include "yacas/lispatom.h"
#include "yacas/lisperror.h"
#include "yacas/lispenvironment.h"

#include "yacas/lispio.h"
#include "yacas/platfileio.h"
#include "yacas/lispenvironment.h"
#include "yacas/tokenizer.h"
#include "yacas/infixparser.h"
#include "yacas/lispeval.h"
#include "yacas/stringio.h"
#include "yacas/numbers.h"

bool InternalIsList(const LispPtr& aPtr)
{
    if (!aPtr)
        return false;
    if (!aPtr->SubList())
        return false;
    if (!(*aPtr->SubList()))
        return false;
    // The following happens with IsList(UnList({foo(x), a, b}))
    if ((*aPtr->SubList())->String()->c_str() == nullptr)
        return false;
    //TODO this StrEqual is far from perfect. We could pass in a LispEnvironment object...
    if (std::strcmp((*aPtr->SubList())->String()->c_str(), "List"))
        return false;
    return true;
}

bool InternalIsString(LispString * aOriginal)
{
    if (aOriginal)
        if ((*aOriginal)[0] == '\"')
            if ((*aOriginal)[aOriginal->size()-1] == '\"')
                return true;
    return false;
}

/* TODO: in documenting the choices made in the C++ engine, perhaps document why I pass objects that should contain the
   result by reference. Result string passed in by reference to avoid copy-constructors etcetera (allowing
   the code to share the same LispString in different places).
 */
void InternalUnstringify(LispString& aResult, const LispString * aOriginal)
{
  /*TODO: should these not be checked also higher up, and should this not be an assert at this level?
   * ideally this function should be as efficient as possible (allowing for a code generator to generate
   * compiled code that calls this function immediately. The compiler could prove that the input is valid,
   * so the checks would not be needed here in a non-debug run).
   *
   * Also do not forget to make the change in the Java version then, and find the other places where this is relevant.
   */
    if (!aOriginal || aOriginal->size() < 2 || aOriginal->front() != '\"' || aOriginal->back() != '\"')
        throw LispErrInvalidArg();

    aResult.assign(aOriginal->c_str() + 1, aOriginal->size() - 2);
}

void InternalStringify(LispString& aResult, const LispString* aOriginal)
{
    if (!aOriginal)
        throw LispErrInvalidArg();

    LispInt nrc=aOriginal->size()-1;
    aResult.resize(nrc+3);
    LispInt i;
    aResult[0] = '\"';
    for (i=0;i<nrc;i++)
        aResult[i+1] = (*aOriginal)[i];
    aResult[nrc+1] = '\"';
    aResult[nrc+2] = '\0';
}

void InternalIntToAscii(LispChar * aTrg,LispInt aInt)
{
    LispInt ind=0;
    if (aInt < 0)
    {
        *aTrg++ = '-';
        aInt = -aInt;
    }
    while (aInt != 0)
    {
        aTrg[ind] = '0'+(aInt%10);
        ind++;
        aInt/=10;
    }

    //zero
    if (ind == 0)
    {
        aTrg[0] = '0';
        ind++;
    }
    aTrg[ind]='\0';
    LispInt i;
    for (i=0;i<(ind>>1);i++)
    {
        LispChar swap=aTrg[i];
        aTrg[i] = aTrg[ind-i-1];
        aTrg[ind-i-1] = swap;
    }
}

LispInt InternalAsciiToInt(const LispString& aString)
{
    const LispChar* ptr = aString.c_str();

    if (!IsNumber(ptr, false))
        throw LispErrInvalidArg();

    return std::atoi(ptr);
}

bool IsNumber(const LispChar * ptr,bool aAllowFloat)
{
    if (*ptr == '-' || *ptr == '+')
        ptr++;

    LispInt nrDigits=0;
    LispInt index=0;
    while(ptr[index] >= '0' && ptr[index] <= '9')
    {
        nrDigits++;
        index++;
    }
    if (ptr[index] == '.')
    {
        if (!aAllowFloat)
            return false;
        index++;
        while(ptr[index] >= '0' && ptr[index] <= '9')
        {
            nrDigits++;
            index++;
        }
    }
    if (nrDigits == 0)
        return false;
    if (ptr[index] == 'e' || ptr[index] == 'E')
    {
        if (!aAllowFloat)
            return false;
        if (!NumericSupportForMantissa())
            return false;
        index++;
        if (ptr[index] == '-' || ptr[index] == '+') index++;
        while(ptr[index] >= '0' && ptr[index] <= '9') index++;
    }
    if (ptr[index] != '\0') return false;
    return true;
}

void InternalNth(LispPtr& aResult, const LispPtr& aArg, LispInt n)
{
    if (n < 0 || !aArg || !aArg->SubList())
        throw LispErrInvalidArg();

    LispIterator iter(*aArg->SubList());

    while (n>0)
    {
        if (!iter.getObj())
            throw LispErrInvalidArg();

        ++iter;
        n--;
    }

    if (!iter.getObj())
        throw LispErrInvalidArg();

    aResult = (iter.getObj()->Copy());
}

void InternalTail(LispPtr& aResult, const LispPtr& aArg)
{
    if (!aArg)
        throw LispErrInvalidArg();

    LispPtr* iter = aArg->SubList();

    if (!iter || !*iter)
        throw LispErrInvalidArg();

    aResult = (LispSubList::New((*iter)->Nixed()));
}

void InternalReverseList(LispPtr& aResult, const LispPtr& aOriginal)
{
    LispPtr iter(aOriginal);
    LispPtr previous;
    LispPtr tail(aOriginal);

    while (!!iter)
    {
        tail = iter->Nixed();
        iter->Nixed() = (previous);
        previous = iter;
        iter = tail;
    }
    aResult = previous;
}

void InternalFlatCopy(LispPtr& aResult, const LispPtr& aOriginal)
{
    LispConstIterator orig(aOriginal);
    LispIterator res(aResult);

    while (orig.getObj())
    {
        (*res) = (orig.getObj()->Copy());
        ++orig;
        ++res;
    }
}

LispInt InternalListLength(const LispPtr& aOriginal)
{
    LispConstIterator iter(aOriginal);
    LispInt length = 0;
    while (iter.getObj())
    {
        /*
         if (iter()->String())
            printf("%s ",iter()->String()->c_str());
         */
        ++iter;
        length++;
    }
    return length;
}

bool InternalEquals(LispEnvironment& aEnvironment,
                    const LispPtr& aExpression1,
                    const LispPtr& aExpression2)
{
    // Handle pointers to same, or nullptr
    if (aExpression1.ptr() == aExpression2.ptr())  // compare pointers to LispObject
    {
        return true;
    }

/*TODO This code would be better, if BigNumber::Equals works*/

    BigNumber *n1 = aExpression1->Number(aEnvironment.Precision());
    BigNumber *n2 = aExpression2->Number(aEnvironment.Precision());
    if (!(!n1 && !n2) )
    {
        if (n1 == n2)
        {
            return true;
        }
        if (!n1) return false;
        if (!n2) return false;
        if (n1->Equals(*n2)) return true;
//this should be enabled
        return false;
    }

    //Pointers to strings should be the same
    if (aExpression1->String() != aExpression2->String())
    {
        return false;
    }

    // Handle same sublists, or nullptr
    if (aExpression1->SubList() == aExpression2->SubList())
    {
        return true;
    }

    // Now check the sublists
    if (aExpression1->SubList())
    {
        if (!aExpression2->SubList())
        {
            return false;
        }
        LispIterator iter1(*aExpression1->SubList());
        LispIterator iter2(*aExpression2->SubList());

        while (iter1.getObj() && iter2.getObj())
        {
            // compare two list elements
            if (!InternalEquals(aEnvironment, *iter1, *iter2))
            {
                return false;
            }

            // Step to next
            ++iter1;
            ++iter2;
        }
        // Lists don't have the same length
        if (iter1.getObj() != iter2.getObj())
            return false;

        // Same!
        return true;
    }

    // expressions sublists are not the same!
    return false;
}

void DoInternalLoad(LispEnvironment& aEnvironment,LispInput* aInput)
{
    LispLocalInput localInput(aEnvironment, aInput);

    // TODO make "EndOfFile" a global thing
    // read-parse-eval to the end of file
    LispString * eof = aEnvironment.HashTable().LookUp("EndOfFile");
    bool endoffile = false;

    LispTokenizer tok;
    InfixParser parser(tok,
                       *aEnvironment.CurrentInput(),
                       aEnvironment,
                       aEnvironment.PreFix(),
                       aEnvironment.InFix(),
                       aEnvironment.PostFix(),
                       aEnvironment.Bodied());

    while (!endoffile)
    {
        LispPtr readIn;
        // Read expression
        parser.Parse(readIn);

        if (!readIn)
            throw LispErrReadingFile();

        // Check for end of file
        if (readIn->String() == eof)
        {
            endoffile = true;
            }
        // Else evaluate
        else
        {
            LispPtr result;
            aEnvironment.iEvaluator->Eval(aEnvironment, result, readIn);
        }
    }
}

void InternalLoad(LispEnvironment& aEnvironment, const LispString* aFileName)
{
    LispString oper;
    InternalUnstringify(oper, aFileName);

    LispString * contents = aEnvironment.FindCachedFile(oper.c_str());
    LispString * hashedname = aEnvironment.HashTable().LookUp(oper.c_str());

    InputStatus oldstatus = aEnvironment.iInputStatus;
    aEnvironment.iInputStatus.SetTo(hashedname->c_str());

    if (contents)
    {
        StringInput newInput(*contents,aEnvironment.iInputStatus);
        DoInternalLoad(aEnvironment,&newInput);
        delete contents;
    }
    else
    {
        //TODO make the file api platform independent!!!!
        // Open file
        LispLocalFile localFP(aEnvironment, hashedname->c_str(),true,
                              aEnvironment.iInputDirectories);

        if (!localFP.stream.is_open())
            throw LispErrFileNotFound();

        CachedStdFileInput newInput(localFP,aEnvironment.iInputStatus);
        DoInternalLoad(aEnvironment,&newInput);
    }
    aEnvironment.iInputStatus.RestoreFrom(oldstatus);
}

void InternalUse(LispEnvironment& aEnvironment,LispString * aFileName)
{
    LispDefFile* def = aEnvironment.DefFiles().File(aFileName);
    if (!def->IsLoaded())
    {
        def->SetLoaded();
        InternalLoad(aEnvironment,aFileName);
    }
}

void InternalApplyString(LispEnvironment& aEnvironment, LispPtr& aResult,
                 LispString * aOperator,LispPtr& aArgs)
{
    if (!InternalIsString(aOperator))
        throw LispErrNotString();

    LispObject *head =
        LispAtom::New(aEnvironment,SymbolName(aEnvironment, aOperator->c_str())->c_str());
    head->Nixed() = (aArgs);
    LispPtr body(LispSubList::New(head));
    aEnvironment.iEvaluator->Eval(aEnvironment, aResult, body);
}

void InternalApplyPure(LispPtr& oper,LispPtr& args2,LispPtr& aResult,LispEnvironment& aEnvironment)
{
    LispPtr * chk1 = oper->SubList();

    if (!chk1)
        throw LispErrInvalidArg();

    LispPtr oper2((*chk1)->Nixed());

    if (!oper2)
        throw LispErrInvalidArg();

    LispPtr body(oper2->Nixed());

    if (!body)
        throw LispErrInvalidArg();

    LispPtr * chk2 = oper2->SubList();

    if (!chk2 || !*chk2)
        throw LispErrInvalidArg();

    oper2 = ((*chk2)->Nixed());

    LispLocalFrame frame(aEnvironment,false);

    while (!!oper2)  {
        if (!args2)
            throw LispErrInvalidArg();

        LispString* var = oper2->String();

        if (!var)
            throw LispErrInvalidArg();

        LispPtr newly(args2->Copy());

        aEnvironment.NewLocal(var,newly);

        oper2 = (oper2->Nixed());

        args2 = (args2->Nixed());
    }

    if (args2)
        throw LispErrInvalidArg();

    aEnvironment.iEvaluator->Eval(aEnvironment, aResult, body);
}

void InternalEvalString(LispEnvironment& aEnvironment, LispPtr& aResult,
                        const LispChar* aString)
{
    LispString full(aString);
    full.push_back(';');
    StringInput input(full,aEnvironment.iInputStatus);
    LispPtr lispexpr;
    LispTokenizer &tok = *aEnvironment.iCurrentTokenizer;
    InfixParser parser(tok, input,
                       aEnvironment,
                       aEnvironment.PreFix(),
                       aEnvironment.InFix(),
                       aEnvironment.PostFix(),
                       aEnvironment.Bodied());
    parser.Parse(lispexpr);

    aEnvironment.iEvaluator->Eval(aEnvironment, aResult, lispexpr);
}

LispObject* operator+(const LispObjectAdder& left, const LispObjectAdder& right)
{
  LispObject* trav = left.iPtr;
  while (!!trav->Nixed())
  {
        trav = trav->Nixed();
  }
  trav->Nixed() = (right.iPtr);
  return left.iPtr;
}

void ParseExpression(LispPtr& aResult, const LispChar* aString,LispEnvironment& aEnvironment)
{
    LispString full(aString);
    full.push_back(';');
    StringInput input(full,aEnvironment.iInputStatus);
    aEnvironment.iInputStatus.SetTo("String");
    LispTokenizer &tok = *aEnvironment.iCurrentTokenizer;
    InfixParser parser(tok,
                       input,
                       aEnvironment,
                       aEnvironment.PreFix(),
                       aEnvironment.InFix(),
                       aEnvironment.PostFix(),
                       aEnvironment.Bodied());
    parser.Parse(aResult);
}

void ReturnUnEvaluated(LispPtr& aResult,LispPtr& aArguments,
                       LispEnvironment& aEnvironment)
{
    LispPtr full(aArguments->Copy());
    aResult = (LispSubList::New(full));

    LispIterator iter(aArguments);
    ++iter;

    while (iter.getObj())
    {
        LispPtr next;
        aEnvironment.iEvaluator->Eval(aEnvironment, next, *iter);
        full->Nixed() = (next);
        full = (next);
        ++iter;
    }
    full->Nixed() = (nullptr);
}

void PrintExpression(LispString& aResult,
                     LispPtr& aExpression,
                     LispEnvironment& aEnvironment,
                     std::size_t aMaxChars)
{
    aResult.clear();
    StringOutput newOutput(aResult);
    InfixPrinter infixprinter(aEnvironment.PreFix(),
                              aEnvironment.InFix(),
                              aEnvironment.PostFix(),
                              aEnvironment.Bodied());
    infixprinter.Print(aExpression, newOutput, aEnvironment);
    if (aMaxChars > 0 && aResult.size()>aMaxChars)
    {
        aResult.resize(aMaxChars - 3);
        aResult += "...";
    }
}

LispString* SymbolName(LispEnvironment& aEnvironment,
                       const LispChar * aSymbol)
{
    if (aSymbol[0] == '\"')
        return aEnvironment.HashTable().LookUpCounted(aSymbol + 1, std::strlen(aSymbol) - 2);
    else
        return aEnvironment.HashTable().LookUp(aSymbol);
}

<|endoftext|>


<commit_before>#include <v8.h>
#include "macros.h"
#include "gc.h"

#ifdef windows
#	include <my_global.h>
#endif

#include <mysql.h>
#include <stdlib.h>
#include <string>

#define MYSQL_ERROR mysql_error(conn)
#define ASSERT_CONNECTED if (!conn) { return JS_EXCEPTION("No connection established yet."); }
#define MYSQL_PTR MYSQL * conn = LOAD_PTR(0, MYSQL *)

namespace {

v8::Persistent<v8::FunctionTemplate> rest;

void destroy(v8::Handle<v8::Object> obj) {
	v8::Handle<v8::Function> fun = v8::Handle<v8::Function>::Cast(obj->Get(JS_STR("close")));
	fun->Call(obj, 0, NULL);
}

v8::Handle<v8::Value> createResult(MYSQL * conn) {
	MYSQL_RES * res = mysql_store_result(conn);

	if (res) {
		v8::Handle<v8::Value> resargs[] = { v8::External::New((void *) res) };
		return rest->GetFunction()->NewInstance(1, resargs);
	} else {
		if (mysql_field_count(conn)) {
			return JS_EXCEPTION(MYSQL_ERROR);
		} else {
			return JS_BOOL(true);
		}
	}
}

/**
 * MySQL constructor does basically nothing. It just adds "this.close()" method to global GC
 */
JS_METHOD(_mysql) {
	ASSERT_CONSTRUCTOR;
	SAVE_PTR(0, NULL);
	GC * gc = GC_PTR;
	gc->add(args.This(), destroy);
	return args.This();
}

/**
 * Close DB connection
 */
JS_METHOD(_close) {
	MYSQL_PTR;
	if (conn) {
		mysql_close(conn);
		SAVE_PTR(0, NULL);
	}
	return args.This();
}

/**
 * Should be called ASAP: new MySQL().connect("host", "user", "pass", "db")
 */
JS_METHOD(_connect) {
	if (args.Length() < 4) {
		return JS_EXCEPTION("Invalid call format. Use 'mysql.connect(host, user, pass, db)'");
	}

	MYSQL * conn;

	v8::String::Utf8Value host(args[0]);
	v8::String::Utf8Value user(args[1]);
	v8::String::Utf8Value pass(args[2]);
	v8::String::Utf8Value db(args[3]);

	conn = mysql_init(NULL);
	if (!mysql_real_connect(conn, *host, *user, *pass, *db, 0, NULL, CLIENT_MULTI_STATEMENTS)) {
		return JS_EXCEPTION(MYSQL_ERROR);
	} else {
		mysql_query(conn, "SET NAMES 'utf8'");
		SAVE_PTR(0, conn);
		return args.This();
	}
}

/**
 * Query takes a string argument and returns an instance of Result object
 */
JS_METHOD(_query) {
	MYSQL_PTR;
	ASSERT_CONNECTED;
	if (args.Length() < 1) {
		return JS_EXCEPTION("No query specified");
	}
	v8::String::Utf8Value q(args[0]);

	int code = mysql_real_query(conn, *q, q.length());
	if (code != 0) { return JS_EXCEPTION(MYSQL_ERROR); }

	int qc = args.This()->Get(JS_STR("queryCount"))->ToInteger()->Int32Value();
	args.This()->Set(JS_STR("queryCount"), JS_INT(qc+1));

	return createResult(conn);
}

/**
 * Fetch next result from a multi-result set
 */
JS_METHOD(_nextresult) {
	MYSQL_PTR;
	ASSERT_CONNECTED;

	int status = mysql_next_result(conn);

	if (status == -1) { return JS_NULL; }
	if (status > 0) { return JS_EXCEPTION(MYSQL_ERROR); }
	return createResult(conn);
}

JS_METHOD(_affectedrows) {
	MYSQL_PTR;
	ASSERT_CONNECTED;
	return JS_INT(mysql_affected_rows(conn));
}

JS_METHOD(_insertid) {
	MYSQL_PTR;
	ASSERT_CONNECTED;
	return JS_INT(mysql_insert_id(conn));
}

JS_METHOD(_escape) {
	MYSQL_PTR;
	ASSERT_CONNECTED;

	if (args.Length() < 1) {
		return JS_EXCEPTION("Nothing to escape");
	}
	v8::String::Utf8Value str(args[0]);

	int len = args[0]->ToString()->Utf8Length();
	char * result = new char[2*len + 1];

	int length = mysql_real_escape_string(conn, result, *str, len);
	v8::Handle<v8::Value> output = JS_STR(result, length);
	delete[] result;
	return output;
}

JS_METHOD(_qualify) {
	if (args.Length() < 1) {
		return JS_EXCEPTION("Nothing to qualify");
	}

	v8::String::Utf8Value str(args[0]);
	std::string result = "`";
	result += *str;
	result += "`";

	v8::Handle<v8::Value> output = JS_STR(result.c_str());
	return output;
}

JS_METHOD(_result) {
	SAVE_VALUE(0, args[0]);
	return args.This();
}

JS_METHOD(_numrows) {
	MYSQL_RES * res = LOAD_PTR(0, MYSQL_RES *);

	return JS_INT(mysql_num_rows(res));
}

JS_METHOD(_numfields) {
	MYSQL_RES * res = LOAD_PTR(0, MYSQL_RES *);

	return JS_INT(mysql_num_fields(res));
}

JS_METHOD(_fetchnames) {
	MYSQL_RES * res = LOAD_PTR(0, MYSQL_RES *);

	int cnt = mysql_num_fields(res);
	MYSQL_FIELD * fields = mysql_fetch_fields(res);
	v8::Handle<v8::Array> result = v8::Array::New(cnt);

	for(int i = 0; i < cnt; i++) {
		result->Set(JS_INT(i), JS_STR(fields[i].name));
	}

	return result;
}

/**
 * Return result data as an array of JS arrays
 */
JS_METHOD(_fetcharrays) {
	MYSQL_RES * res = LOAD_PTR(0, MYSQL_RES *);
	mysql_data_seek(res, 0);

	int x = mysql_num_fields(res);
	int y = mysql_num_rows(res);
	MYSQL_ROW row;
	v8::Handle<v8::Array> result = v8::Array::New(y);

	for (int i = 0; i < y; i++) {
		row = mysql_fetch_row(res);
		v8::Handle<v8::Array> item = v8::Array::New(x);
		result->Set(JS_INT(i), item);
		for (int j=0; j<x; j++) {
			if (row[j] == NULL) {
				item->Set(JS_INT(j), v8::Null());
			} else {
				item->Set(JS_INT(j), JS_STR(row[j]));
			}
		}
	}

	return result;
}

/**
 * Return result data as an array of JS objects, indexed with column names
 */
JS_METHOD(_fetchobjects) {
	MYSQL_RES * res = LOAD_PTR(0, MYSQL_RES *);
	mysql_data_seek(res, 0);

	int x = mysql_num_fields(res);
	int y = mysql_num_rows(res);
	MYSQL_FIELD * fields = mysql_fetch_fields(res);
	MYSQL_ROW row;
	v8::Handle<v8::Array> result = v8::Array::New(y);

	for (int i = 0; i < y; i++) {
		row = mysql_fetch_row(res);
		v8::Handle<v8::Object> item = v8::Object::New();
		result->Set(JS_INT(i), item);
		for (int j=0; j<x; j++) {
			if (row[j] == NULL) {
				item->Set(JS_STR(fields[j].name), v8::Null());
			} else {
				item->Set(JS_STR(fields[j].name), JS_STR(row[j]));
			}
		}
	}

	return result;
}

} /* end namespace */

SHARED_INIT() {
	v8::HandleScope handle_scope;
	v8::Handle<v8::FunctionTemplate> ft = v8::FunctionTemplate::New(_mysql);
	ft->SetClassName(JS_STR("MySQL"));

	v8::Handle<v8::ObjectTemplate> ot = ft->InstanceTemplate();
	ot->SetInternalFieldCount(1); /* connection */

	/**
	 * Static property, useful for stats gathering
	 */
	ot->Set(JS_STR("queryCount"), JS_INT(0));

	v8::Handle<v8::ObjectTemplate> pt = ft->PrototypeTemplate();

	/**
	 * MySQL prototype methods (new MySQL().*)
	 */
	pt->Set(JS_STR("connect"), v8::FunctionTemplate::New(_connect));
	pt->Set(JS_STR("close"), v8::FunctionTemplate::New(_close));
	pt->Set(JS_STR("query"), v8::FunctionTemplate::New(_query));
	pt->Set(JS_STR("nextResult"), v8::FunctionTemplate::New(_nextresult));
	pt->Set(JS_STR("affectedRows"), v8::FunctionTemplate::New(_affectedrows));
	pt->Set(JS_STR("escape"), v8::FunctionTemplate::New(_escape));
	pt->Set(JS_STR("qualify"), v8::FunctionTemplate::New(_qualify));
	pt->Set(JS_STR("insertId"), v8::FunctionTemplate::New(_insertid));

	rest = v8::Persistent<v8::FunctionTemplate>::New(v8::FunctionTemplate::New(_result));
	rest->SetClassName(JS_STR("Result"));

	v8::Handle<v8::ObjectTemplate> resinst = rest->InstanceTemplate();
	resinst->SetInternalFieldCount(1);

	v8::Handle<v8::ObjectTemplate> resproto = rest->PrototypeTemplate();

	/**
	 * Result prototype methods (new MySQL().query().*)
	 */
	resproto->Set(JS_STR("numRows"), v8::FunctionTemplate::New(_numrows));
	resproto->Set(JS_STR("numFields"), v8::FunctionTemplate::New(_numfields));
	resproto->Set(JS_STR("fetchNames"), v8::FunctionTemplate::New(_fetchnames));
	resproto->Set(JS_STR("fetchArrays"), v8::FunctionTemplate::New(_fetcharrays));
	resproto->Set(JS_STR("fetchObjects"), v8::FunctionTemplate::New(_fetchobjects));

	exports->Set(JS_STR("MySQL"), ft->GetFunction());
}
<commit_msg>mysql result close()<commit_after>#include <v8.h>
#include "macros.h"
#include "gc.h"

#ifdef windows
#	include <my_global.h>
#endif

#include <mysql.h>
#include <stdlib.h>
#include <string>

#define MYSQL_ERROR mysql_error(conn)
#define ASSERT_CONNECTED if (!conn) { return JS_EXCEPTION("No connection established yet."); }
#define MYSQL_PTR MYSQL * conn = LOAD_PTR(0, MYSQL *)

namespace {

v8::Persistent<v8::FunctionTemplate> rest;

void destroy(v8::Handle<v8::Object> obj) {
	v8::Handle<v8::Function> fun = v8::Handle<v8::Function>::Cast(obj->Get(JS_STR("close")));
	fun->Call(obj, 0, NULL);
}

v8::Handle<v8::Value> createResult(MYSQL * conn) {
	MYSQL_RES * res = mysql_store_result(conn);

	if (res) {
		v8::Handle<v8::Value> resargs[] = { v8::External::New((void *) res) };
		return rest->GetFunction()->NewInstance(1, resargs);
	} else {
		if (mysql_field_count(conn)) {
			return JS_EXCEPTION(MYSQL_ERROR);
		} else {
			return JS_BOOL(true);
		}
	}
}

/**
 * MySQL constructor does basically nothing. It just adds "this.close()" method to global GC
 */
JS_METHOD(_mysql) {
	ASSERT_CONSTRUCTOR;
	SAVE_PTR(0, NULL);
	GC * gc = GC_PTR;
	gc->add(args.This(), destroy);
	return args.This();
}

/**
 * Close DB connection
 */
JS_METHOD(_close) {
	MYSQL_PTR;
	if (conn) {
		mysql_close(conn);
		SAVE_PTR(0, NULL);
	}
	return args.This();
}

/**
 * Should be called ASAP: new MySQL().connect("host", "user", "pass", "db")
 */
JS_METHOD(_connect) {
	if (args.Length() < 4) {
		return JS_EXCEPTION("Invalid call format. Use 'mysql.connect(host, user, pass, db)'");
	}

	MYSQL * conn;

	v8::String::Utf8Value host(args[0]);
	v8::String::Utf8Value user(args[1]);
	v8::String::Utf8Value pass(args[2]);
	v8::String::Utf8Value db(args[3]);

	conn = mysql_init(NULL);
	if (!mysql_real_connect(conn, *host, *user, *pass, *db, 0, NULL, CLIENT_MULTI_STATEMENTS)) {
		return JS_EXCEPTION(MYSQL_ERROR);
	} else {
		mysql_query(conn, "SET NAMES 'utf8'");
		SAVE_PTR(0, conn);
		return args.This();
	}
}

/**
 * Query takes a string argument and returns an instance of Result object
 */
JS_METHOD(_query) {
	MYSQL_PTR;
	ASSERT_CONNECTED;
	if (args.Length() < 1) {
		return JS_EXCEPTION("No query specified");
	}
	v8::String::Utf8Value q(args[0]);

	int code = mysql_real_query(conn, *q, q.length());
	if (code != 0) { return JS_EXCEPTION(MYSQL_ERROR); }

	int qc = args.This()->Get(JS_STR("queryCount"))->ToInteger()->Int32Value();
	args.This()->Set(JS_STR("queryCount"), JS_INT(qc+1));

	return createResult(conn);
}

/**
 * Fetch next result from a multi-result set
 */
JS_METHOD(_nextresult) {
	MYSQL_PTR;
	ASSERT_CONNECTED;

	int status = mysql_next_result(conn);

	if (status == -1) { return JS_NULL; }
	if (status > 0) { return JS_EXCEPTION(MYSQL_ERROR); }
	return createResult(conn);
}

JS_METHOD(_affectedrows) {
	MYSQL_PTR;
	ASSERT_CONNECTED;
	return JS_INT(mysql_affected_rows(conn));
}

JS_METHOD(_insertid) {
	MYSQL_PTR;
	ASSERT_CONNECTED;
	return JS_INT(mysql_insert_id(conn));
}

JS_METHOD(_escape) {
	MYSQL_PTR;
	ASSERT_CONNECTED;

	if (args.Length() < 1) {
		return JS_EXCEPTION("Nothing to escape");
	}
	v8::String::Utf8Value str(args[0]);

	int len = args[0]->ToString()->Utf8Length();
	char * result = new char[2*len + 1];

	int length = mysql_real_escape_string(conn, result, *str, len);
	v8::Handle<v8::Value> output = JS_STR(result, length);
	delete[] result;
	return output;
}

JS_METHOD(_qualify) {
	if (args.Length() < 1) {
		return JS_EXCEPTION("Nothing to qualify");
	}

	v8::String::Utf8Value str(args[0]);
	std::string result = "`";
	result += *str;
	result += "`";

	v8::Handle<v8::Value> output = JS_STR(result.c_str());
	return output;
}

JS_METHOD(_result) {
	SAVE_VALUE(0, args[0]);
	return args.This();
}

JS_METHOD(_numrows) {
	MYSQL_RES * res = LOAD_PTR(0, MYSQL_RES *);

	return JS_INT(mysql_num_rows(res));
}

JS_METHOD(_numfields) {
	MYSQL_RES * res = LOAD_PTR(0, MYSQL_RES *);

	return JS_INT(mysql_num_fields(res));
}

JS_METHOD(_fetchnames) {
	MYSQL_RES * res = LOAD_PTR(0, MYSQL_RES *);

	int cnt = mysql_num_fields(res);
	MYSQL_FIELD * fields = mysql_fetch_fields(res);
	v8::Handle<v8::Array> result = v8::Array::New(cnt);

	for(int i = 0; i < cnt; i++) {
		result->Set(JS_INT(i), JS_STR(fields[i].name));
	}

	return result;
}

/**
 * Return result data as an array of JS arrays
 */
JS_METHOD(_fetcharrays) {
	MYSQL_RES * res = LOAD_PTR(0, MYSQL_RES *);
	mysql_data_seek(res, 0);

	int x = mysql_num_fields(res);
	int y = mysql_num_rows(res);
	MYSQL_ROW row;
	v8::Handle<v8::Array> result = v8::Array::New(y);

	for (int i = 0; i < y; i++) {
		row = mysql_fetch_row(res);
		v8::Handle<v8::Array> item = v8::Array::New(x);
		result->Set(JS_INT(i), item);
		for (int j=0; j<x; j++) {
			if (row[j] == NULL) {
				item->Set(JS_INT(j), v8::Null());
			} else {
				item->Set(JS_INT(j), JS_STR(row[j]));
			}
		}
	}

	return result;
}

/**
 * Return result data as an array of JS objects, indexed with column names
 */
JS_METHOD(_fetchobjects) {
	MYSQL_RES * res = LOAD_PTR(0, MYSQL_RES *);
	mysql_data_seek(res, 0);

	int x = mysql_num_fields(res);
	int y = mysql_num_rows(res);
	MYSQL_FIELD * fields = mysql_fetch_fields(res);
	MYSQL_ROW row;
	v8::Handle<v8::Array> result = v8::Array::New(y);

	for (int i = 0; i < y; i++) {
		row = mysql_fetch_row(res);
		v8::Handle<v8::Object> item = v8::Object::New();
		result->Set(JS_INT(i), item);
		for (int j=0; j<x; j++) {
			if (row[j] == NULL) {
				item->Set(JS_STR(fields[j].name), v8::Null());
			} else {
				item->Set(JS_STR(fields[j].name), JS_STR(row[j]));
			}
		}
	}

	return result;
}

JS_METHOD(_result_close) {
	MYSQL_RES * res = LOAD_PTR(0, MYSQL_RES *);
	if (res) {
		mysql_free_result(res);
		SAVE_PTR(0, NULL);
		return JS_BOOL(true);
	} else {
		return JS_BOOL(false);
	}
}

} /* end namespace */

SHARED_INIT() {
	v8::HandleScope handle_scope;
	v8::Handle<v8::FunctionTemplate> ft = v8::FunctionTemplate::New(_mysql);
	ft->SetClassName(JS_STR("MySQL"));

	v8::Handle<v8::ObjectTemplate> ot = ft->InstanceTemplate();
	ot->SetInternalFieldCount(1); /* connection */

	/**
	 * Static property, useful for stats gathering
	 */
	ot->Set(JS_STR("queryCount"), JS_INT(0));

	v8::Handle<v8::ObjectTemplate> pt = ft->PrototypeTemplate();

	/**
	 * MySQL prototype methods (new MySQL().*)
	 */
	pt->Set(JS_STR("connect"), v8::FunctionTemplate::New(_connect));
	pt->Set(JS_STR("close"), v8::FunctionTemplate::New(_close));
	pt->Set(JS_STR("query"), v8::FunctionTemplate::New(_query));
	pt->Set(JS_STR("nextResult"), v8::FunctionTemplate::New(_nextresult));
	pt->Set(JS_STR("affectedRows"), v8::FunctionTemplate::New(_affectedrows));
	pt->Set(JS_STR("escape"), v8::FunctionTemplate::New(_escape));
	pt->Set(JS_STR("qualify"), v8::FunctionTemplate::New(_qualify));
	pt->Set(JS_STR("insertId"), v8::FunctionTemplate::New(_insertid));

	rest = v8::Persistent<v8::FunctionTemplate>::New(v8::FunctionTemplate::New(_result));
	rest->SetClassName(JS_STR("Result"));

	v8::Handle<v8::ObjectTemplate> resinst = rest->InstanceTemplate();
	resinst->SetInternalFieldCount(1);

	v8::Handle<v8::ObjectTemplate> resproto = rest->PrototypeTemplate();

	/**
	 * Result prototype methods (new MySQL().query().*)
	 */
	resproto->Set(JS_STR("numRows"), v8::FunctionTemplate::New(_numrows));
	resproto->Set(JS_STR("numFields"), v8::FunctionTemplate::New(_numfields));
	resproto->Set(JS_STR("fetchNames"), v8::FunctionTemplate::New(_fetchnames));
	resproto->Set(JS_STR("fetchArrays"), v8::FunctionTemplate::New(_fetcharrays));
	resproto->Set(JS_STR("fetchObjects"), v8::FunctionTemplate::New(_fetchobjects));
	resproto->Set(JS_STR("close"), v8::FunctionTemplate::New(_result_close));

	exports->Set(JS_STR("MySQL"), ft->GetFunction());
}
<|endoftext|>


<commit_before>#include <fstream>
#include <iostream>
#include <string>
#include <vector>

#include "naivebayes.h"

std::tuple<std::vector<NB::Event>, std::vector<NB::label_t>>
load_data(const std::string &filename, const std::size_t num_features) {
  std::vector<NB::Event> events;
  std::vector<NB::label_t> labels;
  std::vector<NB::number_t> features(num_features);
  NB::label_t label;

  std::ifstream ifs(filename);
  while (ifs.good()) {
    for (std::size_t f = 0; f < num_features; f++) {
      ifs >> features[f];
    }
    ifs >> label;
    events.push_back(NB::Event{features});
    labels.push_back(label);
  }
  ifs.close();

  return std::make_tuple(events, labels);
}

int main(int argc, char *argv[]) {
  if (argc < 4) {
    std::cerr << "Usage: " << argv[0] << " N train predict" << std::endl
              << "  N       - number of features" << std::endl
              << "  train   - path to the training data sample" << std::endl
              << "  predict - path to the data sample to be predicted"
              << std::endl;
    return EXIT_FAILURE;
  }

  const size_t num_features = std::stoul(argv[1]);
  const std::string filename_train = std::string(argv[2]),
                    filename_predict = std::string(argv[3]);

  NB::NaiveBayes nb(num_features);
  std::vector<NB::Event> events;
  std::vector<NB::label_t> labels;

  // Training
  std::tie(events, labels) = load_data(filename_train, num_features);
  nb.train(events, labels);

  // Testing (write results to file, actual evaluation is done via Python)
  std::tie(events, labels) = load_data(filename_predict, num_features);
  for (const auto &e : events) {
    std::cout << nb.predict(e) << std::endl;
  }
}
<commit_msg>Update main.cc<commit_after>#include <fstream>
#include <iostream>
#include <string>
#include <vector>

#include "naivebayes.h"

std::tuple<std::vector<NB::Event>, std::vector<NB::label_t>>
load_data(const std::string &filename, const std::size_t num_features) {
  std::vector<NB::Event> events;
  std::vector<NB::label_t> labels;
  std::vector<NB::number_t> features(num_features);
  NB::label_t label;

  std::ifstream ifs(filename);
  while (ifs.good()) {
    for (std::size_t f = 0; f < num_features; f++) {
      ifs >> features[f];
    }
    ifs >> label;
    events.push_back(NB::Event{features});
    labels.push_back(label);
  }
  ifs.close();

  return std::make_tuple(events, labels);
}

int main(int argc, char *argv[]) {
  if (argc < 4) {
    std::cerr << "Usage: " << argv[0] << " N train predict" << std::endl
              << "  N       - number of features" << std::endl
              << "  train   - path to the training data sample" << std::endl
              << "  predict - path to the data sample to be predicted"
              << std::endl;
    return EXIT_FAILURE;
  }

  const size_t num_features = std::stoul(argv[1]);
  const std::string filename_train = std::string(argv[2]),
                    filename_predict = std::string(argv[3]);

  NB::NaiveBayes nb(num_features);
  std::vector<NB::Event> events;
  std::vector<NB::label_t> labels;

  // Training
  std::tie(events, labels) = load_data(filename_train, num_features);
  nb.train(events, labels);

  // Prediction
  std::tie(events, labels) = load_data(filename_predict, num_features);
  for (const auto &e : events) {
    std::cout << nb.predict(e) << std::endl;
  }
}
<|endoftext|>


<commit_before>// Copyright (c) 2013 GitHub, Inc. All rights reserved.
// Use of this source code is governed by MIT license that can be found in the
// LICENSE file.

#include <string.h>
#include <stdlib.h>

#include "rescle.h"

bool print_error(const char* message) {
  fprintf(stderr, "Fatal error: %s\n", message);
  return 1;
}

bool parse_version_string(const wchar_t* str, int *v1, int *v2, int *v3, int *v4) {
  *v1 = *v2 = *v3 = *v4 = 0;
  if (swscanf_s(str, L"%d.%d.%d.%d", v1, v2, v3, v4) == 4)
    return true;
  if (swscanf_s(str, L"%d.%d.%d", v1, v2, v3) == 3)
    return true;
  if (swscanf_s(str, L"%d.%d", v1, v2) == 2)
    return true;
  if (swscanf_s(str, L"%d", v1) == 1)
    return true;

  return false;
}

int wmain(int argc, const wchar_t* argv[]) {
  bool loaded = false;
  rescle::ResourceUpdater updater;

  for (int i = 1; i < argc; ++i) {
    if (wcscmp(argv[i], L"--set-version-string") == 0 ||
        wcscmp(argv[i], L"-svs") == 0) {
      if (argc - i < 3)
        return print_error("--set-version-string requires 'Key' and 'Value'");

      const wchar_t* key = argv[++i];
      const wchar_t* value = argv[++i];
      if (!updater.ChangeVersionString(key, value))
        return print_error("Unable to change version string");
    } else if (wcscmp(argv[i], L"--set-file-version") == 0 ||
               wcscmp(argv[i], L"-sfv") == 0) {
      if (argc - i < 2)
        return print_error("--set-file-version requires a version string");

      int v1, v2, v3, v4;
      if (!parse_version_string(argv[++i], &v1, &v2, &v3, &v4))
        return print_error("Unable to parse version string");

      if (!updater.ChangeVersionFileVersion(v1, v2, v3, v4))
        return print_error("Unable to change file version");
    } else if (wcscmp(argv[i], L"--set-product-version") == 0 ||
               wcscmp(argv[i], L"-spv") == 0) {
      if (argc - i < 2)
        return print_error("--set-product-version requires a version string");

      int v1, v2, v3, v4;
      if (!parse_version_string(argv[++i], &v1, &v2, &v3, &v4))
        return print_error("Unable to parse version string");

      if (!updater.ChangeVersionProductVersion(v1, v2, v3, v4))
        return print_error("Unable to change product version");
    } else {
      if (loaded)
        return print_error("Unexpected trailing arguments");

      loaded = true;
      if (!updater.Load(argv[i]))
        return print_error("Unable to load file");
    }
  }

  if (!loaded)
    return print_error("You should specify a exe/dll file");

  if (!updater.Commit())
    return print_error("Unable to commit changes");

  return 0;
}
<commit_msg>Also set version string when setting file version and product version.<commit_after>// Copyright (c) 2013 GitHub, Inc. All rights reserved.
// Use of this source code is governed by MIT license that can be found in the
// LICENSE file.

#include <string.h>
#include <stdlib.h>

#include "rescle.h"

bool print_error(const char* message) {
  fprintf(stderr, "Fatal error: %s\n", message);
  return 1;
}

bool parse_version_string(const wchar_t* str, int *v1, int *v2, int *v3, int *v4) {
  *v1 = *v2 = *v3 = *v4 = 0;
  if (swscanf_s(str, L"%d.%d.%d.%d", v1, v2, v3, v4) == 4)
    return true;
  if (swscanf_s(str, L"%d.%d.%d", v1, v2, v3) == 3)
    return true;
  if (swscanf_s(str, L"%d.%d", v1, v2) == 2)
    return true;
  if (swscanf_s(str, L"%d", v1) == 1)
    return true;

  return false;
}

int wmain(int argc, const wchar_t* argv[]) {
  bool loaded = false;
  rescle::ResourceUpdater updater;

  for (int i = 1; i < argc; ++i) {
    if (wcscmp(argv[i], L"--set-version-string") == 0 ||
        wcscmp(argv[i], L"-svs") == 0) {
      if (argc - i < 3)
        return print_error("--set-version-string requires 'Key' and 'Value'");

      const wchar_t* key = argv[++i];
      const wchar_t* value = argv[++i];
      if (!updater.ChangeVersionString(key, value))
        return print_error("Unable to change version string");
    } else if (wcscmp(argv[i], L"--set-file-version") == 0 ||
               wcscmp(argv[i], L"-sfv") == 0) {
      if (argc - i < 2)
        return print_error("--set-file-version requires a version string");

      int v1, v2, v3, v4;
      if (!parse_version_string(argv[++i], &v1, &v2, &v3, &v4))
        return print_error("Unable to parse version string");

      if (!updater.ChangeVersionFileVersion(v1, v2, v3, v4))
        return print_error("Unable to change file version");

      if (!updater.ChangeVersionString(L"FileVersion", argv[i]))
        return print_error("Unable to change FileVersion string");
    } else if (wcscmp(argv[i], L"--set-product-version") == 0 ||
               wcscmp(argv[i], L"-spv") == 0) {
      if (argc - i < 2)
        return print_error("--set-product-version requires a version string");

      int v1, v2, v3, v4;
      if (!parse_version_string(argv[++i], &v1, &v2, &v3, &v4))
        return print_error("Unable to parse version string");

      if (!updater.ChangeVersionFileVersion(v1, v2, v3, v4))
        return print_error("Unable to change file version");

      if (!updater.ChangeVersionString(L"ProductVersion", argv[i]))
        return print_error("Unable to change ProductVersion string");
    } else {
      if (loaded)
        return print_error("Unexpected trailing arguments");

      loaded = true;
      if (!updater.Load(argv[i]))
        return print_error("Unable to load file");
    }
  }

  if (!loaded)
    return print_error("You should specify a exe/dll file");

  if (!updater.Commit())
    return print_error("Unable to commit changes");

  return 0;
}
<|endoftext|>


<commit_before>// RUN: %clang_cc1 -fsyntax-only -verify -Wno-array-bounds %s -fpascal-strings

void consume(const char* c) {}
void consume(const unsigned char* c) {}
void consume(const wchar_t* c) {}
void consumeChar(char c) {}

enum MyEnum {
  kMySmallEnum = 1,
  kMyEnum = 5
};

enum OperatorOverloadEnum {
  kMyOperatorOverloadedEnum = 5
};

const char* operator+(const char* c, OperatorOverloadEnum e) {
  return "yo";
}

const char* operator+(OperatorOverloadEnum e, const char* c) {
  return "yo";
}

void f(int index) {
  // Should warn.
  consume("foo" + 5);  // expected-warning {{adding 'int' to a string does not append to the string}} expected-note {{use array indexing to silence this warning}}
  consume("foo" + index);  // expected-warning {{adding 'int' to a string does not append to the string}} expected-note {{use array indexing to silence this warning}}
  consume("foo" + kMyEnum);  // expected-warning {{adding 'MyEnum' to a string does not append to the string}} expected-note {{use array indexing to silence this warning}}

  consume(5 + "foo");  // expected-warning {{adding 'int' to a string does not append to the string}} expected-note {{use array indexing to silence this warning}}
  consume(index + "foo");  // expected-warning {{adding 'int' to a string does not append to the string}} expected-note {{use array indexing to silence this warning}}
  consume(kMyEnum + "foo");  // expected-warning {{adding 'MyEnum' to a string does not append to the string}} expected-note {{use array indexing to silence this warning}}

  // FIXME: suggest replacing with "foo"[5]
  consumeChar(*("foo" + 5));  // expected-warning {{adding 'int' to a string does not append to the string}} expected-note {{use array indexing to silence this warning}}
  consumeChar(*(5 + "foo"));  // expected-warning {{adding 'int' to a string does not append to the string}} expected-note {{use array indexing to silence this warning}}

  consume(L"foo" + 5);  // expected-warning {{adding 'int' to a string does not append to the string}} expected-note {{use array indexing to silence this warning}}

  // Should not warn.
  consume(&("foo"[3]));
  consume(&("foo"[index]));
  consume(&("foo"[kMyEnum]));
  consume("foo" + kMySmallEnum);
  consume(kMySmallEnum + "foo");

  consume(L"foo" + 2);

  consume("foo" + 3);  // Points at the \0
  consume("foo" + 4);  // Points 1 past the \0, which is legal too.
  consume("\pfoo" + 4);  // Pascal strings don't have a trailing \0, but they
                         // have a leading length byte, so this is fine too.

  consume("foo" + kMyOperatorOverloadedEnum);
  consume(kMyOperatorOverloadedEnum + "foo");

  #define A "foo"
  #define B "bar"
  consume(A B + sizeof(A) - 1);
}

<commit_msg>Add a test for the -Wstring-plus-int fixit note.<commit_after>// RUN: %clang_cc1 -fsyntax-only -verify -Wno-array-bounds %s -fpascal-strings
// RUN: %clang_cc1 -fdiagnostics-parseable-fixits -x c++ %s 2>&1 -Wno-array-bounds -fpascal-strings | FileCheck %s

void consume(const char* c) {}
void consume(const unsigned char* c) {}
void consume(const wchar_t* c) {}
void consumeChar(char c) {}

enum MyEnum {
  kMySmallEnum = 1,
  kMyEnum = 5
};

enum OperatorOverloadEnum {
  kMyOperatorOverloadedEnum = 5
};

const char* operator+(const char* c, OperatorOverloadEnum e) {
  return "yo";
}

const char* operator+(OperatorOverloadEnum e, const char* c) {
  return "yo";
}

void f(int index) {
  // Should warn.
  // CHECK: fix-it:"{{.*}}":{31:11-31:11}:"&"
  // CHECK: fix-it:"{{.*}}":{31:17-31:18}:"["
  // CHECK: fix-it:"{{.*}}":{31:20-31:20}:"]"
  consume("foo" + 5);  // expected-warning {{adding 'int' to a string does not append to the string}} expected-note {{use array indexing to silence this warning}}
  consume("foo" + index);  // expected-warning {{adding 'int' to a string does not append to the string}} expected-note {{use array indexing to silence this warning}}
  consume("foo" + kMyEnum);  // expected-warning {{adding 'MyEnum' to a string does not append to the string}} expected-note {{use array indexing to silence this warning}}

  consume(5 + "foo");  // expected-warning {{adding 'int' to a string does not append to the string}} expected-note {{use array indexing to silence this warning}}
  consume(index + "foo");  // expected-warning {{adding 'int' to a string does not append to the string}} expected-note {{use array indexing to silence this warning}}
  consume(kMyEnum + "foo");  // expected-warning {{adding 'MyEnum' to a string does not append to the string}} expected-note {{use array indexing to silence this warning}}

  // FIXME: suggest replacing with "foo"[5]
  consumeChar(*("foo" + 5));  // expected-warning {{adding 'int' to a string does not append to the string}} expected-note {{use array indexing to silence this warning}}
  consumeChar(*(5 + "foo"));  // expected-warning {{adding 'int' to a string does not append to the string}} expected-note {{use array indexing to silence this warning}}

  consume(L"foo" + 5);  // expected-warning {{adding 'int' to a string does not append to the string}} expected-note {{use array indexing to silence this warning}}

  // Should not warn.
  consume(&("foo"[3]));
  consume(&("foo"[index]));
  consume(&("foo"[kMyEnum]));
  consume("foo" + kMySmallEnum);
  consume(kMySmallEnum + "foo");

  consume(L"foo" + 2);

  consume("foo" + 3);  // Points at the \0
  consume("foo" + 4);  // Points 1 past the \0, which is legal too.
  consume("\pfoo" + 4);  // Pascal strings don't have a trailing \0, but they
                         // have a leading length byte, so this is fine too.

  consume("foo" + kMyOperatorOverloadedEnum);
  consume(kMyOperatorOverloadedEnum + "foo");

  #define A "foo"
  #define B "bar"
  consume(A B + sizeof(A) - 1);
}

<|endoftext|>


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

#include "DiceRoll/Operations/RollResult.h"
#include "DiceRoll/Operations/RepeatOperation.h"
#include "DiceRoll/Operations/RandomOperation.hpp"

class MockOperation : public IOperation
{
    std::unique_ptr<RollResult> execute()
	{ return std::make_unique<RollResult>(); }

public:
    static int count;

    MockOperation(): IOperation(nullptr){};

    // For each evaluate() static int count is incrementing.
    std::unique_ptr<RollResult> evaluate()
	{
	    auto result = std::make_unique<RollResult>();
	    result->setFullResult(std::to_string(++count));
	    result->setShortResult(count);
	    return result;
	}
};
int MockOperation::count = 0;
MockOperation baseOp;

// Test to check if repeating even works (without the same result).
TEST(RepeatOperationTest, EvaluateWithPositiveCount)
{
    MockOperation::count = 0;
    RepeatOperation repeatOp(&baseOp , 3);

    auto result = repeatOp.evaluate();
    EXPECT_EQ("(1 2 3)", result->getFullResult());

    EXPECT_EQ(std::vector<int>({1, 2, 3}), result->getShortResult());
    EXPECT_FALSE(result->hasErrors());
}

TEST(RepeatOperationTest, CantInitializeWithCountLowerThan2)
{
  MockOperation::count = 0;

  ASSERT_ANY_THROW(RepeatOperation(&baseOp, 1));
  ASSERT_ANY_THROW(RepeatOperation(&baseOp, -1));
}

TEST(RepeatOperationTest, RepeatRandomOperation)
{
    RandomOperation op(10);
    RepeatOperation repeatOp(&op, 100);
    auto result = repeatOp.evaluate();

    ASSERT_EQ(100, result->getShortResultSize());
    for(auto item: result->getShortResult()) {
	EXPECT_GE(item, 1);
	EXPECT_LE(item, 10);
    }
}

//TODO: Add test that makes sure that RepeatOperation won't work on
//      multiple value operation, just singular value.
TEST(RepeatOperationTest, CanRepeatRepeatOperation)
{
  MockOperation::count = 0;

  RepeatOperation repOp(&baseOp, 3);
  ASSERT_NO_THROW(RepeatOperation(&repOp, 3));
  RepeatOperation repOpFinal = RepeatOperation(&repOp, 3);
  auto result = repOpFinal.evaluate();

  EXPECT_EQ(result->getFullResult(), "((1 2 3) (4 5 6) (7 8 9))");
  EXPECT_EQ(result->getShortResult(),
            std::vector<int>({1, 2, 3, 4, 5, 6, 7, 8, 9}));
}
<commit_msg>Removed obsolete comment.<commit_after>#include "gtest/gtest.h"

#include "DiceRoll/Operations/RollResult.h"
#include "DiceRoll/Operations/RepeatOperation.h"
#include "DiceRoll/Operations/RandomOperation.hpp"

class MockOperation : public IOperation
{
    std::unique_ptr<RollResult> execute()
	{ return std::make_unique<RollResult>(); }

public:
    static int count;

    MockOperation(): IOperation(nullptr){};

    // For each evaluate() static int count is incrementing.
    std::unique_ptr<RollResult> evaluate()
	{
	    auto result = std::make_unique<RollResult>();
	    result->setFullResult(std::to_string(++count));
	    result->setShortResult(count);
	    return result;
	}
};
int MockOperation::count = 0;
MockOperation baseOp;

// Test to check if repeating even works (without the same result).
TEST(RepeatOperationTest, EvaluateWithPositiveCount)
{
    MockOperation::count = 0;
    RepeatOperation repeatOp(&baseOp , 3);

    auto result = repeatOp.evaluate();
    EXPECT_EQ("(1 2 3)", result->getFullResult());

    EXPECT_EQ(std::vector<int>({1, 2, 3}), result->getShortResult());
    EXPECT_FALSE(result->hasErrors());
}

TEST(RepeatOperationTest, CantInitializeWithCountLowerThan2)
{
  MockOperation::count = 0;

  ASSERT_ANY_THROW(RepeatOperation(&baseOp, 1));
  ASSERT_ANY_THROW(RepeatOperation(&baseOp, -1));
}

TEST(RepeatOperationTest, RepeatRandomOperation)
{
    RandomOperation op(10);
    RepeatOperation repeatOp(&op, 100);
    auto result = repeatOp.evaluate();

    ASSERT_EQ(100, result->getShortResultSize());
    for(auto item: result->getShortResult()) {
	EXPECT_GE(item, 1);
	EXPECT_LE(item, 10);
    }
}

TEST(RepeatOperationTest, CanRepeatRepeatOperation)
{
  MockOperation::count = 0;

  RepeatOperation repOp(&baseOp, 3);
  ASSERT_NO_THROW(RepeatOperation(&repOp, 3));
  RepeatOperation repOpFinal = RepeatOperation(&repOp, 3);
  auto result = repOpFinal.evaluate();

  EXPECT_EQ(result->getFullResult(), "((1 2 3) (4 5 6) (7 8 9))");
  EXPECT_EQ(result->getShortResult(),
            std::vector<int>({1, 2, 3, 4, 5, 6, 7, 8, 9}));
}
<|endoftext|>


<commit_before>/*
* Copyright 2016 Nu-book Inc.
* Copyright 2017 Axel Waggershauser
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#include "ByteArray.h"
#include "BlackboxTestRunner.h"
#include "ImageLoader.h"
#include "MultiFormatReader.h"
#include "Result.h"
#include "BinaryBitmap.h"
#include "ImageLoader.h"
#include "DecodeHints.h"
#include "TextUtfEncoding.h"
#include "ZXContainerAlgorithms.h"
#include "ZXFilesystem.h"

#include <cstdlib>
#include <cstring>
#include <iostream>
#include <fstream>
#include <set>

using namespace ZXing;
using namespace ZXing::Test;

int getEnv(const char* name, int fallback = 0)
{
	auto var = getenv(name);
	return var ? atoi(var) : fallback;
}

int main(int argc, char** argv)
{
	if (argc <= 1) {
		std::cout << "Usage: " << argv[0] << " <test_path_prefix>" << std::endl;
		return 0;
	}

	fs::path pathPrefix = argv[1];

	if (Contains({".png", ".jpg", ".pgm", ".gif"}, pathPrefix.extension())) {
		auto hints = DecodeHints().setTryHarder(true).setTryRotate(true).setIsPure(getEnv("IS_PURE"));
//		hints.setFormats(BarcodeFormat::QR_CODE);
		MultiFormatReader reader(hints);
		int rotation = getEnv("ROTATION");

		for (int i = 1; i < argc; ++i) {
			Result result = reader.read(*ImageLoader::load(argv[i]).rotated(rotation));
			std::cout << argv[i] << ": ";
			if (result.isValid())
				std::cout << ToString(result.format()) << ": " << TextUtfEncoding::ToUtf8(result.text()) << " " << metadataToUtf8(result) << "\n";
			else
				std::cout << "FAILED\n";
			if (result.isValid() && getenv("WRITE_TEXT")) {
				std::ofstream f(fs::path(argv[i]).replace_extension(".txt"));
				f << TextUtfEncoding::ToUtf8(result.text());
			}
		}
		return 0;
	} else {
		std::set<std::string> includedTests;
		for (int i = 2; i < argc; ++i) {
			if (std::strlen(argv[i]) > 2 && argv[i][0] == '-' && argv[i][1] == 't') {
				includedTests.insert(argv[i] + 2);
			}
		}

		return runBlackBoxTests(pathPrefix, includedTests);
	}
}
<commit_msg>TestReader: allow setting Formats from environment variable<commit_after>/*
* Copyright 2016 Nu-book Inc.
* Copyright 2017 Axel Waggershauser
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#include "ByteArray.h"
#include "BlackboxTestRunner.h"
#include "ImageLoader.h"
#include "MultiFormatReader.h"
#include "Result.h"
#include "BinaryBitmap.h"
#include "ImageLoader.h"
#include "DecodeHints.h"
#include "TextUtfEncoding.h"
#include "ZXContainerAlgorithms.h"
#include "ZXFilesystem.h"

#include <cstdlib>
#include <cstring>
#include <iostream>
#include <fstream>
#include <set>

using namespace ZXing;
using namespace ZXing::Test;

int getEnv(const char* name, int fallback = 0)
{
	auto var = getenv(name);
	return var ? atoi(var) : fallback;
}

int main(int argc, char** argv)
{
	if (argc <= 1) {
		std::cout << "Usage: " << argv[0] << " <test_path_prefix>" << std::endl;
		return 0;
	}

	fs::path pathPrefix = argv[1];

	if (Contains({".png", ".jpg", ".pgm", ".gif"}, pathPrefix.extension())) {
		auto hints = DecodeHints().setTryHarder(true).setTryRotate(true).setIsPure(getEnv("IS_PURE"));
		if (getenv("FORMATS"))
			hints.setFormats(BarcodeFormatsFromString(getenv("FORMATS")));
		MultiFormatReader reader(hints);
		int rotation = getEnv("ROTATION");

		for (int i = 1; i < argc; ++i) {
			Result result = reader.read(*ImageLoader::load(argv[i]).rotated(rotation));
			std::cout << argv[i] << ": ";
			if (result.isValid())
				std::cout << ToString(result.format()) << ": " << TextUtfEncoding::ToUtf8(result.text()) << " " << metadataToUtf8(result) << "\n";
			else
				std::cout << "FAILED\n";
			if (result.isValid() && getenv("WRITE_TEXT")) {
				std::ofstream f(fs::path(argv[i]).replace_extension(".txt"));
				f << TextUtfEncoding::ToUtf8(result.text());
			}
		}
		return 0;
	} else {
		std::set<std::string> includedTests;
		for (int i = 2; i < argc; ++i) {
			if (std::strlen(argv[i]) > 2 && argv[i][0] == '-' && argv[i][1] == 't') {
				includedTests.insert(argv[i] + 2);
			}
		}

		return runBlackBoxTests(pathPrefix, includedTests);
	}
}
<|endoftext|>


<commit_before>//===-- ipcd.cpp ----------------------------------------------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// Implements IPCD interface, communicates with ipcd via control socket
//
//===----------------------------------------------------------------------===//

#include "ipcd.h"

#include "debug.h"
#include "real.h"
#include "rename_fd.h"
#include "magic_socket_nums.h"
#include "lock.h"

#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/un.h>
#include <unistd.h>

static int ipcd_socket = 0;
static int mypid = 0;

// Must match that used by server,
// currently defined in ipcd/clients.go
const char *SOCK_PATH = "/tmp/ipcd.sock";
const char *IPCD_BIN_PATH = "/bin/ipcd";

const useconds_t SLEEP_AFTER_IPCD_START_INTERVAL = 10 * 1000; // 10ms?

SimpleLock &getConnectLock() {
  static SimpleLock ConnectLock;
  return ConnectLock;
}

bool we_are_ipcd;

void check_if_we_are_ipcd() {
  // man 3 program_invocation_name
  extern char *program_invocation_short_name;
  assert(program_invocation_short_name);

  ipclog("program_invocation_short_name: %s\n", program_invocation_short_name);

  we_are_ipcd = strcmp("ipcd", program_invocation_short_name) == 0;
}

void start_ipcd() {
  int d = daemon(0, 0);
  if (d == -1) {
    perror("Failed to daemonize");
    exit(1);
  }

  execl(IPCD_BIN_PATH, IPCD_BIN_PATH, (char *)NULL);
  perror("Failed to exec ipcd");
  exit(1);
}

void fork_ipcd() {
  switch (__real_fork()) {
  case -1:
    break;
  case 0:
    // Child
    start_ipcd();
    assert(0 && "Exec failed?");
    break;
  default:
    // Wait for ipcd to start :P
    ipclog("Starting ipcd...\n");
  }
}

void connect_to_ipcd() {
  int s, len;
  struct sockaddr_un remote;

  if ((s = __real_socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0)) == -1) {
    perror("socket");
    exit(1);
  }
  bool rename_success = rename_fd(s, MAGIC_SOCKET_FD, /* cloexec */ true);
  assert(rename_success);
  s = MAGIC_SOCKET_FD;

  remote.sun_family = AF_UNIX;
  strcpy(remote.sun_path, SOCK_PATH);
  len = strlen(remote.sun_path) + sizeof(remote.sun_family);
  if (__real_connect(s, (struct sockaddr *)&remote, len) == -1) {
    // XXX: Super kludge!
    // If we can't connect to daemon, assume it hasn't been
    // started yet and attempt to run it ourselves.
    // This code is terrible, and is likely to break
    // badly in many situations, but works for now.
    if (errno == ENOENT || errno == ECONNREFUSED) {
      fork_ipcd();

      int attempts = 0;
      const int MAX_ATTEMPTS = 10;
      while (attempts < MAX_ATTEMPTS) {
        usleep(SLEEP_AFTER_IPCD_START_INTERVAL);
        if (__real_connect(s, (struct sockaddr *)&remote, len) != -1)
          break;
        ++attempts;
      }
      if (attempts == MAX_ATTEMPTS) {
        perror("Unable to connect to ipcd after attempting to start it");
        exit(1);
      }
    } else {
      perror("connect");
      ipclog("Error connecting to ipcd?\n");
      exit(1);
    }
  }

  ipclog("Connected to IPCD, fd=%d\n", s);
  mypid = getpid();
  ipcd_socket = s;
}

void __ipcd_init() {
  assert(ipcd_socket == 0);
  check_if_we_are_ipcd();
  if (ipcd_enabled())
    connect_to_ipcd();
}

void __attribute__((destructor)) ipcd_dtor() {
  if (ipcd_socket == 0) {
    ipclog("Exiting without establishing connection to ipcd...\n");
    return;
  }
  assert(ipcd_socket != 0);
  assert(mypid != 0);

  // TODO: REMOVEALL is goodness, but we don't presently
  // correctly associate PID's with endpoints they own in ipcd.
  // Partially due to the mechanism for REREGISTER being
  // performed 'eagerly' before the child PID is known,
  // and partially due to a need to update ipcd's datastructures.

  // This needs to be done eventually anyway, to provide
  // access control restrictions on who can unregister,
  // optimize, etc. a given endpoint.

  // For now, explicitly unregister all endpoints we know we own.
  // XXX: This is done in ipcopt.cpp since this code
  // doesn't have access to our state table, just ipcd.

  // Return instead of REMOVEALL to avoid
  // duplicate UNREGISTER.
  return;

  char buf[100];
  int len = sprintf(buf, "REMOVEALL %d\n", mypid);
  assert(len > 5);
  int err = __real_send(ipcd_socket, buf, len, MSG_NOSIGNAL);
  if (err < 0) {
    ipclog("Error sending ipcd REMOVEALL command in dtor: %s\n",
           strerror(errno));
    return;
  }
  err = __real_recv(ipcd_socket, buf, 50, MSG_NOSIGNAL);
  assert(err > 5);

  const char *match = "200 REMOVED ";
  size_t matchlen = strlen(match);
  if (size_t(err) < matchlen) {
    ipclog("Error receiving response from ipcd in dtor: %s\n", strerror(errno));
    return;
  }
  bool success = (strncmp(buf, "200 REMOVED ", matchlen) == 0);
  if (!success) {
    ipclog("Failure, response=%s\n", buf);
  }
  if (success)
    ipclog("Successfully unregistered all fd's\n");
  else
    ipclog("Failed to remove all fd's\n");
}

void connect_if_needed() {
  if (mypid == getpid())
    return;

  ipclog("Reconnecting to ipcd in child...\n");
  __real_close(ipcd_socket);
  connect_to_ipcd();
}

endpoint ipcd_register_socket(int fd) {
  ScopedLock L(getConnectLock());
  connect_if_needed();

  char buf[100];
  int len = sprintf(buf, "REGISTER %d %d\n", getpid(), fd);
  assert(len > 5);

  assert(ipcd_socket);
  // ipclog("REGISTER %d -->\n", fd);
  int err = __real_send(ipcd_socket, buf, len, MSG_NOSIGNAL);
  if (err < 0) {
    perror("write");
    exit(1);
  }

  // ipclog("REGISTER %d <--\n", fd);
  err = __real_recv(ipcd_socket, buf, 50, MSG_NOSIGNAL);
  assert(err > 5);

  buf[err] = 0;
  int id;
  int n = sscanf(buf, "200 ID %d\n", &id);
  assert(n == 1);

  // ipclog("Registered and got endpoint id=%d\n", id);

  return id;
}

bool ipcd_localize(endpoint local, endpoint remote) {
  ScopedLock L(getConnectLock());
  connect_if_needed();

  char buf[100];
  int len = sprintf(buf, "LOCALIZE %d %d\n", local, remote);
  assert(len > 5);
  int err = __real_send(ipcd_socket, buf, len, MSG_NOSIGNAL);
  if (err < 0) {
    perror("write");
    exit(1);
  }
  err = __real_recv(ipcd_socket, buf, 50, MSG_NOSIGNAL);
  assert(err > 5);

  return strncmp(buf, "200 OK\n", err) == 0;
}

// GETLOCALFD
int ipcd_getlocalfd(endpoint local) {
  ScopedLock L(getConnectLock());
  connect_if_needed();

  char buf[100];
  int len = sprintf(buf, "GETLOCALFD %d\n", local);
  assert(len > 5);
  int err = __real_send(ipcd_socket, buf, len, MSG_NOSIGNAL);
  if (err < 0) {
    perror("write");
    exit(1);
  }

  int fd;
  {
    // Lots of magic
    const size_t CONTROLLEN = CMSG_LEN(sizeof(int));
    struct iovec iov[1];
    struct msghdr msg;
    struct cmsghdr *cmsg;
    char cmsg_buf[CONTROLLEN];
    cmsg = (struct cmsghdr *)cmsg_buf;

    memset(&msg, 0, sizeof(msg));

    iov[0].iov_base = buf;
    iov[0].iov_len = sizeof(buf);
    msg.msg_iov = iov;
    msg.msg_iovlen = 1;
    msg.msg_control = cmsg;
    msg.msg_controllen = CONTROLLEN;

    int ret = recvmsg(ipcd_socket, &msg, MSG_NOSIGNAL);
    if (ret <= 0) {
      perror("recvmsg");
      exit(1);
    }

    // TODO: Understand and fix mismatch between
    // CONTROLLEN and msg.msg_controllen after recvmsg()!
    memcpy(&fd, CMSG_DATA(cmsg), sizeof(int));
    ipclog("received local fd %d for endpoint %d\n", fd, local);
  }

  err = __real_recv(ipcd_socket, buf, 50, MSG_NOSIGNAL);
  assert(err > 5);

  bool success = strncmp(buf, "200 OK\n", err) == 0;
  assert(success);

  return fd;
}

// UNREGISTER
bool ipcd_unregister_socket(endpoint ep) {
  ScopedLock L(getConnectLock());
  connect_if_needed();

  char buf[100];
  int len = sprintf(buf, "UNREGISTER %d\n", ep);
  assert(len > 5);
  int err = __real_send(ipcd_socket, buf, len, MSG_NOSIGNAL);
  if (err < 0) {
    perror("write");
    exit(1);
  }
  err = __real_recv(ipcd_socket, buf, 50, MSG_NOSIGNAL);
  assert(err > 5);

  return strncmp(buf, "200 OK\n", err) == 0;
}

// REREGISTER
bool ipcd_reregister_socket(endpoint ep, int fd) {
  ScopedLock L(getConnectLock());
  connect_if_needed();

  char buf[100];
  int len = sprintf(buf, "REREGISTER %d %d %d\n", ep, getpid(), fd);
  assert(len > 5);
  int err = __real_send(ipcd_socket, buf, len, MSG_NOSIGNAL);
  if (err < 0) {
    perror("write");
    exit(1);
  }
  err = __real_recv(ipcd_socket, buf, 50, MSG_NOSIGNAL);
  assert(err > 5);

  return strncmp(buf, "200 OK\n", err) == 0;
}

endpoint ipcd_endpoint_kludge(endpoint local) {
  ScopedLock L(getConnectLock());
  connect_if_needed();

  char buf[100];
  int len = sprintf(buf, "ENDPOINT_KLUDGE %d\n", local);
  assert(len > 5);
  int err = __real_send(ipcd_socket, buf, len, MSG_NOSIGNAL);
  if (err < 0) {
    perror("write");
    exit(1);
  }
  err = __real_recv(ipcd_socket, buf, 50, MSG_NOSIGNAL);
  assert(err > 5);

  buf[err] = 0;
  int id;
  ipclog("endpoint_kludge(%d) = %s\n", local, buf);
  int n = sscanf(buf, "200 PAIR %d\n", &id);
  if (n == 1) {
    return id;
  }

  return EP_INVALID;
}

endpoint ipcd_crc_kludge(endpoint local, uint32_t s_crc, uint32_t r_crc) {
  ScopedLock L(getConnectLock());
  connect_if_needed();

  char buf[100];
  int len = sprintf(buf, "THRESH_CRC_KLUDGE %d %d %d\n", local, s_crc, r_crc);
  assert(len > 5);
  int err = __real_send(ipcd_socket, buf, len, MSG_NOSIGNAL);
  if (err < 0) {
    perror("write");
    exit(1);
  }
  err = __real_recv(ipcd_socket, buf, 50, MSG_NOSIGNAL);
  assert(err > 5);

  buf[err] = 0;
  int id;
  ipclog("crc_kludge(%d, %d, %d) = %s\n", local, s_crc, r_crc, buf);
  int n = sscanf(buf, "200 PAIR %d\n", &id);
  if (n == 1) {
    return id;
  }

  return EP_INVALID;
}

bool ipcd_is_protected(int fd) {
  return fd == ipcd_socket;
}

bool ipcd_enabled() {
  static bool disabled = getenv("IPCD_DISABLE") || we_are_ipcd;
  return !disabled;
}
<commit_msg>libipc/ipcd: Be sure to call unwrapped version of recvmsg.<commit_after>//===-- ipcd.cpp ----------------------------------------------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// Implements IPCD interface, communicates with ipcd via control socket
//
//===----------------------------------------------------------------------===//

#include "ipcd.h"

#include "debug.h"
#include "real.h"
#include "rename_fd.h"
#include "magic_socket_nums.h"
#include "lock.h"

#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/un.h>
#include <unistd.h>

static int ipcd_socket = 0;
static int mypid = 0;

// Must match that used by server,
// currently defined in ipcd/clients.go
const char *SOCK_PATH = "/tmp/ipcd.sock";
const char *IPCD_BIN_PATH = "/bin/ipcd";

const useconds_t SLEEP_AFTER_IPCD_START_INTERVAL = 10 * 1000; // 10ms?

SimpleLock &getConnectLock() {
  static SimpleLock ConnectLock;
  return ConnectLock;
}

bool we_are_ipcd;

void check_if_we_are_ipcd() {
  // man 3 program_invocation_name
  extern char *program_invocation_short_name;
  assert(program_invocation_short_name);

  ipclog("program_invocation_short_name: %s\n", program_invocation_short_name);

  we_are_ipcd = strcmp("ipcd", program_invocation_short_name) == 0;
}

void start_ipcd() {
  int d = daemon(0, 0);
  if (d == -1) {
    perror("Failed to daemonize");
    exit(1);
  }

  execl(IPCD_BIN_PATH, IPCD_BIN_PATH, (char *)NULL);
  perror("Failed to exec ipcd");
  exit(1);
}

void fork_ipcd() {
  switch (__real_fork()) {
  case -1:
    break;
  case 0:
    // Child
    start_ipcd();
    assert(0 && "Exec failed?");
    break;
  default:
    // Wait for ipcd to start :P
    ipclog("Starting ipcd...\n");
  }
}

void connect_to_ipcd() {
  int s, len;
  struct sockaddr_un remote;

  if ((s = __real_socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0)) == -1) {
    perror("socket");
    exit(1);
  }
  bool rename_success = rename_fd(s, MAGIC_SOCKET_FD, /* cloexec */ true);
  assert(rename_success);
  s = MAGIC_SOCKET_FD;

  remote.sun_family = AF_UNIX;
  strcpy(remote.sun_path, SOCK_PATH);
  len = strlen(remote.sun_path) + sizeof(remote.sun_family);
  if (__real_connect(s, (struct sockaddr *)&remote, len) == -1) {
    // XXX: Super kludge!
    // If we can't connect to daemon, assume it hasn't been
    // started yet and attempt to run it ourselves.
    // This code is terrible, and is likely to break
    // badly in many situations, but works for now.
    if (errno == ENOENT || errno == ECONNREFUSED) {
      fork_ipcd();

      int attempts = 0;
      const int MAX_ATTEMPTS = 10;
      while (attempts < MAX_ATTEMPTS) {
        usleep(SLEEP_AFTER_IPCD_START_INTERVAL);
        if (__real_connect(s, (struct sockaddr *)&remote, len) != -1)
          break;
        ++attempts;
      }
      if (attempts == MAX_ATTEMPTS) {
        perror("Unable to connect to ipcd after attempting to start it");
        exit(1);
      }
    } else {
      perror("connect");
      ipclog("Error connecting to ipcd?\n");
      exit(1);
    }
  }

  ipclog("Connected to IPCD, fd=%d\n", s);
  mypid = getpid();
  ipcd_socket = s;
}

void __ipcd_init() {
  assert(ipcd_socket == 0);
  check_if_we_are_ipcd();
  if (ipcd_enabled())
    connect_to_ipcd();
}

void __attribute__((destructor)) ipcd_dtor() {
  if (ipcd_socket == 0) {
    ipclog("Exiting without establishing connection to ipcd...\n");
    return;
  }
  assert(ipcd_socket != 0);
  assert(mypid != 0);

  // TODO: REMOVEALL is goodness, but we don't presently
  // correctly associate PID's with endpoints they own in ipcd.
  // Partially due to the mechanism for REREGISTER being
  // performed 'eagerly' before the child PID is known,
  // and partially due to a need to update ipcd's datastructures.

  // This needs to be done eventually anyway, to provide
  // access control restrictions on who can unregister,
  // optimize, etc. a given endpoint.

  // For now, explicitly unregister all endpoints we know we own.
  // XXX: This is done in ipcopt.cpp since this code
  // doesn't have access to our state table, just ipcd.

  // Return instead of REMOVEALL to avoid
  // duplicate UNREGISTER.
  return;

  char buf[100];
  int len = sprintf(buf, "REMOVEALL %d\n", mypid);
  assert(len > 5);
  int err = __real_send(ipcd_socket, buf, len, MSG_NOSIGNAL);
  if (err < 0) {
    ipclog("Error sending ipcd REMOVEALL command in dtor: %s\n",
           strerror(errno));
    return;
  }
  err = __real_recv(ipcd_socket, buf, 50, MSG_NOSIGNAL);
  assert(err > 5);

  const char *match = "200 REMOVED ";
  size_t matchlen = strlen(match);
  if (size_t(err) < matchlen) {
    ipclog("Error receiving response from ipcd in dtor: %s\n", strerror(errno));
    return;
  }
  bool success = (strncmp(buf, "200 REMOVED ", matchlen) == 0);
  if (!success) {
    ipclog("Failure, response=%s\n", buf);
  }
  if (success)
    ipclog("Successfully unregistered all fd's\n");
  else
    ipclog("Failed to remove all fd's\n");
}

void connect_if_needed() {
  if (mypid == getpid())
    return;

  ipclog("Reconnecting to ipcd in child...\n");
  __real_close(ipcd_socket);
  connect_to_ipcd();
}

endpoint ipcd_register_socket(int fd) {
  ScopedLock L(getConnectLock());
  connect_if_needed();

  char buf[100];
  int len = sprintf(buf, "REGISTER %d %d\n", getpid(), fd);
  assert(len > 5);

  assert(ipcd_socket);
  // ipclog("REGISTER %d -->\n", fd);
  int err = __real_send(ipcd_socket, buf, len, MSG_NOSIGNAL);
  if (err < 0) {
    perror("write");
    exit(1);
  }

  // ipclog("REGISTER %d <--\n", fd);
  err = __real_recv(ipcd_socket, buf, 50, MSG_NOSIGNAL);
  assert(err > 5);

  buf[err] = 0;
  int id;
  int n = sscanf(buf, "200 ID %d\n", &id);
  assert(n == 1);

  // ipclog("Registered and got endpoint id=%d\n", id);

  return id;
}

bool ipcd_localize(endpoint local, endpoint remote) {
  ScopedLock L(getConnectLock());
  connect_if_needed();

  char buf[100];
  int len = sprintf(buf, "LOCALIZE %d %d\n", local, remote);
  assert(len > 5);
  int err = __real_send(ipcd_socket, buf, len, MSG_NOSIGNAL);
  if (err < 0) {
    perror("write");
    exit(1);
  }
  err = __real_recv(ipcd_socket, buf, 50, MSG_NOSIGNAL);
  assert(err > 5);

  return strncmp(buf, "200 OK\n", err) == 0;
}

// GETLOCALFD
int ipcd_getlocalfd(endpoint local) {
  ScopedLock L(getConnectLock());
  connect_if_needed();

  char buf[100];
  int len = sprintf(buf, "GETLOCALFD %d\n", local);
  assert(len > 5);
  int err = __real_send(ipcd_socket, buf, len, MSG_NOSIGNAL);
  if (err < 0) {
    perror("write");
    exit(1);
  }

  int fd;
  {
    // Lots of magic
    const size_t CONTROLLEN = CMSG_LEN(sizeof(int));
    struct iovec iov[1];
    struct msghdr msg;
    struct cmsghdr *cmsg;
    char cmsg_buf[CONTROLLEN];
    cmsg = (struct cmsghdr *)cmsg_buf;

    memset(&msg, 0, sizeof(msg));

    iov[0].iov_base = buf;
    iov[0].iov_len = sizeof(buf);
    msg.msg_iov = iov;
    msg.msg_iovlen = 1;
    msg.msg_control = cmsg;
    msg.msg_controllen = CONTROLLEN;

    int ret = __real_recvmsg(ipcd_socket, &msg, MSG_NOSIGNAL);
    if (ret <= 0) {
      perror("recvmsg");
      exit(1);
    }

    // TODO: Understand and fix mismatch between
    // CONTROLLEN and msg.msg_controllen after recvmsg()!
    memcpy(&fd, CMSG_DATA(cmsg), sizeof(int));
    ipclog("received local fd %d for endpoint %d\n", fd, local);
  }

  err = __real_recv(ipcd_socket, buf, 50, MSG_NOSIGNAL);
  assert(err > 5);

  bool success = strncmp(buf, "200 OK\n", err) == 0;
  assert(success);

  return fd;
}

// UNREGISTER
bool ipcd_unregister_socket(endpoint ep) {
  ScopedLock L(getConnectLock());
  connect_if_needed();

  char buf[100];
  int len = sprintf(buf, "UNREGISTER %d\n", ep);
  assert(len > 5);
  int err = __real_send(ipcd_socket, buf, len, MSG_NOSIGNAL);
  if (err < 0) {
    perror("write");
    exit(1);
  }
  err = __real_recv(ipcd_socket, buf, 50, MSG_NOSIGNAL);
  assert(err > 5);

  return strncmp(buf, "200 OK\n", err) == 0;
}

// REREGISTER
bool ipcd_reregister_socket(endpoint ep, int fd) {
  ScopedLock L(getConnectLock());
  connect_if_needed();

  char buf[100];
  int len = sprintf(buf, "REREGISTER %d %d %d\n", ep, getpid(), fd);
  assert(len > 5);
  int err = __real_send(ipcd_socket, buf, len, MSG_NOSIGNAL);
  if (err < 0) {
    perror("write");
    exit(1);
  }
  err = __real_recv(ipcd_socket, buf, 50, MSG_NOSIGNAL);
  assert(err > 5);

  return strncmp(buf, "200 OK\n", err) == 0;
}

endpoint ipcd_endpoint_kludge(endpoint local) {
  ScopedLock L(getConnectLock());
  connect_if_needed();

  char buf[100];
  int len = sprintf(buf, "ENDPOINT_KLUDGE %d\n", local);
  assert(len > 5);
  int err = __real_send(ipcd_socket, buf, len, MSG_NOSIGNAL);
  if (err < 0) {
    perror("write");
    exit(1);
  }
  err = __real_recv(ipcd_socket, buf, 50, MSG_NOSIGNAL);
  assert(err > 5);

  buf[err] = 0;
  int id;
  ipclog("endpoint_kludge(%d) = %s\n", local, buf);
  int n = sscanf(buf, "200 PAIR %d\n", &id);
  if (n == 1) {
    return id;
  }

  return EP_INVALID;
}

endpoint ipcd_crc_kludge(endpoint local, uint32_t s_crc, uint32_t r_crc) {
  ScopedLock L(getConnectLock());
  connect_if_needed();

  char buf[100];
  int len = sprintf(buf, "THRESH_CRC_KLUDGE %d %d %d\n", local, s_crc, r_crc);
  assert(len > 5);
  int err = __real_send(ipcd_socket, buf, len, MSG_NOSIGNAL);
  if (err < 0) {
    perror("write");
    exit(1);
  }
  err = __real_recv(ipcd_socket, buf, 50, MSG_NOSIGNAL);
  assert(err > 5);

  buf[err] = 0;
  int id;
  ipclog("crc_kludge(%d, %d, %d) = %s\n", local, s_crc, r_crc, buf);
  int n = sscanf(buf, "200 PAIR %d\n", &id);
  if (n == 1) {
    return id;
  }

  return EP_INVALID;
}

bool ipcd_is_protected(int fd) {
  return fd == ipcd_socket;
}

bool ipcd_enabled() {
  static bool disabled = getenv("IPCD_DISABLE") || we_are_ipcd;
  return !disabled;
}
<|endoftext|>