<commit_before>#include <osg/Version>
#include <osg/ArgumentParser>
#include <osg/ApplicationUsage>

#include <set>
#include <vector>
#include <iostream>
#include <fstream>

typedef std::pair<std::string, std::string> NamePair;
typedef std::set<NamePair> NameSet;

NamePair EmptyNamePair;

bool validName(const std::string& first)
{
    if (first.empty()) return false;
    if (first[0]<'A' || first[0]>'Z') return false;

    if (first.size()>=2 && (first[1]<'a' || first[1]>'z')) return false;

    if (first=="Added") return false;
    if (first=="CameraNode") return false;
    if (first=="CopyOp") return false;
    if (first=="Fixed") return false;
    if (first=="Creator") return false;
    if (first=="CullVisitor") return false;
    if (first=="Drawable") return false;
    if (first=="Geode") return false;
    if (first=="GeoSet") return false;
    if (first=="Image") return false;
    if (first=="Images/SolarSystem") return false;
    if (first=="IntersectVisitor") return false;
    if (first=="LongIDRecord") return false;
    if (first=="Makefile") return false;
    if (first=="Matrix") return false;
    if (first=="MemoryManager") return false;
    if (first=="MeshRecord") return false;
    if (first=="Multigen") return false;
    if (first=="NewCullVisitor") return false;
    if (first=="Output") return false;
    if (first=="PageLOD") return false;
    if (first=="Improved") return false;
    if (first=="PagedLOD") return false;
    if (first=="Referenced") return false;
    if (first=="StateAttribute") return false;
    if (first=="Switch") return false;
    if (first=="TechniqueEventHandler") return false;
    if (first=="Uniform") return false;
    if (first=="Vec*") return false;
    if (first=="Viewer") return false;
    if (first=="VisualStudio") return false;
    if (first=="X") return false;
    if (first=="Y") return false;
    if (first=="Producer") return false;
    if (first=="New") return false;
    if (first=="Removed") return false;
    if (first=="Ouput") return false;
    if (first=="ReaderWriters") return false;
    if (first=="NodeVisitor") return false;
    if (first=="Fixes") return false;
    if (first=="FontImplementation") return false;
    if (first=="DisplaySettings") return false;
    return true;
}

std::string typoCorrection(const std::string& name)
{
#if 0
    if (name=="") return "";
    if (name=="") return "";
    if (name=="") return "";
#endif
    if (name=="Bistroviae") return "Bistrovic";
    if (name=="Christaiansen") return "Christiansen";
    if (name=="Daust") return "Daoust";
    if (name=="Daved") return "David";
    if (name=="Fred") return "Frederic";
    if (name=="Fredrick") return "Frederic";
    if (name=="Fredric") return "Frederic";
    if (name=="Garrat") return "Garret";
    if (name=="Geof") return "Geoff";
    if (name=="Gronenger") return "Gronager";
    if (name=="Gronger") return "Gronager";
    if (name=="Heirtlein") return "Heirtlein";
    if (name=="Heirtlein") return "Hertlein";
    if (name=="Hertlien") return "Hertlein";
    if (name=="Hi") return "He";
    if (name=="Inverson") return "Iverson";
    if (name=="Iversion") return "Iverson";
    if (name=="Jeoen") return "Joran";
    if (name=="Johhansen") return "Johansen";
    if (name=="Johnansen") return "Johansen";
    if (name=="Johnasen") return "Johansen";
    if (name=="Jolley") return "Jolly";
    if (name=="Jose") return "Jos";
    if (name=="J") return "Jos";
    if (name=="Keuhne") return "Kuehne";
    if (name=="Kheune") return "Kuehne";
    if (name=="Lashari") return "Lashkari";
    if (name=="Laskari") return "Lashkari";
    if (name=="Macro") return "Marco";
    if (name=="Mammond") return "Marmond";
    if (name=="March") return "Marco";
    if (name=="Marz") return "Martz";
    if (name=="Molishtan") return "Molishtan";
    if (name=="Molishtan") return "Moloshtan";
    if (name=="Moloshton") return "Moloshtan";
    if (name=="Moloshton") return "Moloshtan";
    if (name=="Moule") return "Moiule";
    if (name=="Nicklov") return "Nikolov";
    if (name=="Olad") return "Olaf";
    if (name=="Osfied") return "Osfield";
    if (name=="Pail") return "Paul";
    if (name=="Sewel") return "Sewell";
    if (name=="Sjolie") return "Sjlie";
    if (name=="Sokolosky") return "Sokolowsky";
    if (name=="Sokolsky") return "Sokolowsky";
    if (name=="Sonda") return "Sondra";
    if (name=="Stansilav") return "Stanislav";
    if (name=="Stefan") return "Stephan";
    if (name=="Stell") return "Steel";
    if (name=="Tarantilils") return "Tarantilis";
    if (name=="Wieblen") return "Weiblen";
    if (name=="Xennon") return "Hanson";
    if (name=="Yfei") return "Yefei";
    if (name=="Oritz") return "Ortiz";
    return name;
}

void nameCorrection(NamePair& name)
{
    if (name.first=="Eric" && name.second=="Hammil")
    {
        name.first = "Chris";
        name.second = "Hanson";
    }
    if (name.first=="Nick" && name.second=="")
    {
        name.first = "Trajce";
        name.second = "Nikolov";
    }
    if (name.first=="Julia" && name.second=="Ortiz")
    {
        name.first = "Julian";
        name.second = "Ortiz";
    }
}

void lastValidCharacter(const std::string& name, unsigned int& pos,char c)
{
    for(unsigned int i=0;i<pos;++i)
    {
        if (name[i]==c)
        {
            pos = i;
            return;
        }
    }
}

void lastValidCharacter(const std::string& name, unsigned int& last)
{
    lastValidCharacter(name, last, '.');
    lastValidCharacter(name, last, ',');
    lastValidCharacter(name, last, '\'');
    lastValidCharacter(name, last, '/');
    lastValidCharacter(name, last, '\\');
    lastValidCharacter(name, last, ':');
    lastValidCharacter(name, last, ';');
    lastValidCharacter(name, last, ')');
}

NamePair createName(const std::string& first, const std::string& second)
{
    if (first.empty()) return EmptyNamePair;

    unsigned int last = first.size();

    lastValidCharacter(first, last);

    if (last==0) return EmptyNamePair;

    std::string name;

    name.append(first.begin(), first.begin()+last);

    if (!validName(name)) return EmptyNamePair;

    name = typoCorrection(name);

    if (second.empty()) return NamePair(name,"");

    if (!validName(second)) return NamePair(name,"");

    last = second.size();

    lastValidCharacter(second, last);

    if (last>0)
    {
        std::string surname(second.begin(), second.begin()+last);
        surname = typoCorrection(surname);

        return NamePair(name, surname);
    }

    return NamePair(name,"");
}

void readContributors(NameSet& names, const std::string& file)
{
    std::cout<<"readContributions(names,"<<file<<")"<<std::endl;

    std::ifstream fin(file.c_str());

    typedef std::vector< std::string > Words;
    Words words;
    while(fin)
    {
        std::string keyword;
        fin >> keyword;
        words.push_back(keyword);
    }

    std::string blank_string;

    for(unsigned int i=0; i< words.size(); ++i)
    {
        if (words[i]=="From" ||
            words[i]=="from" ||
            words[i]=="From:" ||
            words[i]=="from:")
        {
            if (i+2<words.size() && validName(words[i+1]))
            {
                NamePair name = createName(words[i+1], words[i+2]);
                nameCorrection(name);
                if (!name.first.empty()) names.insert(name);
                i+=2;
            }
            else if (i+1<words.size() && validName(words[i+1]))
            {
                NamePair name = createName(words[i+1], blank_string);
                nameCorrection(name);
                if (!name.first.empty()) names.insert(name);
                i+=1;
            }
        }
    }

    if (names.size()>1)
    {
        for(NameSet::iterator itr = names.begin();
            itr != names.end();
            )
        {
            if (itr->second.empty())
            {
                NameSet::iterator next_itr = itr;
                ++next_itr;

                if (next_itr!=names.end() && itr->first==next_itr->first)
                {
                    names.erase(itr);
                    itr = next_itr;
                }
                else
                {
                    ++itr;
                }
            }
            else
            {
                ++itr;
            }
        }
    }
}

void buildContributors(NameSet& names)
{
    names.insert(NamePair("Robert","Osfield"));
    names.insert(NamePair("Don","Burns"));
    names.insert(NamePair("Marco","Jez"));
}

int main( int argc, char **argv)
{
    osg::ArgumentParser arguments(&argc,argv);
    arguments.getApplicationUsage()->setCommandLineUsage(arguments.getApplicationName()+" [options]");
    arguments.getApplicationUsage()->addCommandLineOption("-h or --help","Display this information");
    arguments.getApplicationUsage()->addCommandLineOption("-c or --contributors","Print out the contributors list.");
    arguments.getApplicationUsage()->addCommandLineOption("-r <file> or --read <file>","Read the changelog to generate an estimated contributors list.");

    printf( "%s\n", osgGetVersion() );

    bool printContributors = false;
    while ( arguments.read("-c") || arguments.read("--contributors")) printContributors = true;

    std::string changeLog;
    while ( arguments.read("-r",changeLog) || arguments.read("--read",changeLog)) printContributors = true;

    // if user request help write it out to cout.
    if (arguments.read("-h") || arguments.read("--help"))
    {
        std::cout<<arguments.getApplicationUsage()->getCommandLineUsage()<<std::endl;
        arguments.getApplicationUsage()->write(std::cout,arguments.getApplicationUsage()->getCommandLineOptions());
        return 1;
    }

    if (printContributors)
    {
        NameSet names;
        buildContributors(names);
        if (!changeLog.empty())
        {
            readContributors(names, changeLog);
        }

        std::cout<<names.size()<<" Contributors:"<<std::endl;
        for(NameSet::iterator itr = names.begin();
            itr != names.end();
            ++itr)
        {
            std::cout<<"  "<<itr->first<<" "<<itr->second<<std::endl;
        }
    }

    return 0;
}
<commit_msg>Added more typo catches.<commit_after>#include <osg/Version>
#include <osg/ArgumentParser>
#include <osg/ApplicationUsage>

#include <set>
#include <vector>
#include <iostream>
#include <fstream>

typedef std::pair<std::string, std::string> NamePair;
typedef std::set<NamePair> NameSet;
typedef std::vector< std::string > Words;

NamePair EmptyNamePair;

bool validName(const std::string& first)
{
    if (first.empty()) return false;
    if (first[0]<'A' || first[0]>'Z') return false;

    if (first.size()>=2 && (first[1]<'a' || first[1]>'z')) return false;

    if (first=="Added") return false;
    if (first=="CameraNode") return false;
    if (first=="CopyOp") return false;
    if (first=="Fixed") return false;
    if (first=="Creator") return false;
    if (first=="CullVisitor") return false;
    if (first=="Drawable") return false;
    if (first=="Geode") return false;
    if (first=="GeoSet") return false;
    if (first=="Image") return false;
    if (first=="Images/SolarSystem") return false;
    if (first=="IntersectVisitor") return false;
    if (first=="LongIDRecord") return false;
    if (first=="Makefile") return false;
    if (first=="Matrix") return false;
    if (first=="MemoryManager") return false;
    if (first=="MeshRecord") return false;
    if (first=="Multigen") return false;
    if (first=="NewCullVisitor") return false;
    if (first=="Output") return false;
    if (first=="PageLOD") return false;
    if (first=="Improved") return false;
    if (first=="PagedLOD") return false;
    if (first=="Referenced") return false;
    if (first=="StateAttribute") return false;
    if (first=="Switch") return false;
    if (first=="TechniqueEventHandler") return false;
    if (first=="Uniform") return false;
    if (first=="Vec*") return false;
    if (first=="Viewer") return false;
    if (first=="VisualStudio") return false;
    if (first=="X") return false;
    if (first=="Y") return false;
    if (first=="Producer") return false;
    if (first=="New") return false;
    if (first=="Removed") return false;
    if (first=="Ouput") return false;
    if (first=="ReaderWriters") return false;
    if (first=="NodeVisitor") return false;
    if (first=="Fixes") return false;
    if (first=="FontImplementation") return false;
    if (first=="DisplaySettings") return false;
    if (first=="AnimationPath") return false;
    if (first=="AnimationPathCallback") return false;
    if (first=="AnimationPathManipulator") return false;
    if (first=="ArgumentParser") return false;
    if (first=="AttrData") return false;
    if (first=="Azimuth") return false;
    if (first=="CluserCullingCallback") return false;
    if (first=="ClusterCullingCallback") return false;
    if (first=="CoordinateSystem") return false;
    if (first=="CoordinateSystemNode") return false;
    if (first=="CoordinateSystemNode&") return false;
    if (first=="Copyright") return false;
    if (first=="Cygwin") return false;
    if (first=="CullCallbacks") return false;
    if (first=="CullingSettngs") return false;
    if (first=="DataVariance") return false;
    if (first=="DatabasePager") return false;
    if (first=="DrawElementsUByte") return false;
    if (first=="Escape") return false;
    if (first=="FluidProgram") return false;
    if (first=="FrameStats") return false;
    if (first=="FreeBSD") return false;
    if (first=="GraphicsContextImplementation") return false;
    if (first=="GraphicsThread") return false;
    if (first=="Images") return false;
    if (first=="IndexBlock") return false;
    if (first=="Inventor") return false;
    if (first=="Make") return false;
    if (first=="Material") return false;
    if (first=="MergeGeometryVisitor") return false;
    if (first=="Mode") return false;
    if (first=="Prodcuer") return false;
    if (first=="ProxyNode") return false;
    if (first=="ReentrantMutex") return false;
    if (first=="ReferenceFrame") return false;
    if (first=="RemoveLoadedProxyNodes") return false;
    if (first=="RenderTargetFallback") return false;
    if (first=="RenderToTextureStage") return false;
    if (first=="Sequence") return false;
    if (first=="Shape") return false;
    if (first=="TessellationHints") return false;
    if (first=="Support") return false;
    if (first=="State") return false;
    if (first=="SmokeTrailEffect") return false;
    if (first=="TexEnv") return false;
    if (first=="Texture3D") return false;
    if (first=="TextureCubeMap") return false;
    if (first=="TextureObjectManager") return false;
    if (first=="TextureRectangle(Image*") return false;
    if (first=="TextureType") return false;
    if (first=="Texuture") return false;
    if (first=="TriStripVisitor") return false;
    if (first=="UserData") return false;
    if (first=="Viewport") return false;
    if (first=="Visual") return false;
    if (first=="Studio") return false;
    if (first=="Vec2d") return false;
    if (first=="Vec3d") return false;
    if (first=="Windows") return false;
    if (first=="Version") return false;
    if (first=="Viewport") return false;
    if (first=="Core") return false;
    if (first=="DataSet") return false;
    if (first=="Endian") return false;
    if (first=="ImageOptions") return false;
    if (first=="ImageStream") return false;
    if (first=="KeyboardMouse") return false;
    if (first=="KeyboardMouseCallback") return false;
    if (first=="AutoTransform") return false;
    if (first=="LightModel") return false;
    if (first=="MatrixManipulator") return false;
    if (first=="MatrixTransform") return false;
    if (first=="OpenDX") return false;
    if (first=="ParentList") return false;
    if (first=="TerraPage") return false;
    if (first=="OveralyNode") return false;
    if (first=="OpenThreads") return false;
    if (first=="PolygonStipple") return false;
    if (first=="SceneView") return false;
    if (first=="PrimitiveIndexFunctor") return false;
    if (first=="PolytopeVisitor") return false;
    if (first=="Performer") return false;
    if (first=="Paging") return false;
    return true;
}

std::string typoCorrection(const std::string& name)
{
#if 0
    if (name=="") return "";
    if (name=="") return "";
    if (name=="") return "";
#endif
    if (name=="Micheal") return "Michael";
    if (name=="Heirtlein") return "Hertlein";
    if (name=="Yefrei") return "Yefei";
    if (name=="Randal") return "Randall";
    if (name=="Hooper") return "Hopper";
    if (name=="Molishtan") return "Moloshtan";
    if (name=="Vines") return "Vine";
    if (name=="Connel") return "Connell";
    if (name=="Bistroviae") return "Bistrovic";
    if (name=="Christaiansen") return "Christiansen";
    if (name=="Daust") return "Daoust";
    if (name=="Daved") return "David";
    if (name=="Fred") return "Frederic";
    if (name=="Fredrick") return "Frederic";
    if (name=="Fredric") return "Frederic";
    if (name=="Garrat") return "Garret";
    if (name=="Geof") return "Geoff";
    if (name=="Gronenger") return "Gronager";
    if (name=="Gronger") return "Gronager";
    if (name=="Heirtlein") return "Heirtlein";
    if (name=="Heirtlein") return "Hertlein";
    if (name=="Hertlien") return "Hertlein";
    if (name=="Hi") return "He";
    if (name=="Inverson") return "Iverson";
    if (name=="Iversion") return "Iverson";
    if (name=="Jeoen") return "Joran";
    if (name=="Johhansen") return "Johansen";
    if (name=="Johnansen") return "Johansen";
    if (name=="Johnasen") return "Johansen";
    if (name=="Jolley") return "Jolly";
    if (name=="Jose") return "Jos";
    if (name=="J") return "Jos";
    if (name=="Keuhne") return "Kuehne";
    if (name=="Kheune") return "Kuehne";
    if (name=="Lashari") return "Lashkari";
    if (name=="Laskari") return "Lashkari";
    if (name=="Macro") return "Marco";
    if (name=="Mammond") return "Marmond";
    if (name=="March") return "Marco";
    if (name=="Marz") return "Martz";
    if (name=="Molishtan") return "Molishtan";
    if (name=="Molishtan") return "Moloshtan";
    if (name=="Moloshton") return "Moloshtan";
    if (name=="Moloshton") return "Moloshtan";
    if (name=="Moule") return "Moiule";
    if (name=="Nicklov") return "Nikolov";
    if (name=="Olad") return "Olaf";
    if (name=="Osfied") return "Osfield";
    if (name=="Pail") return "Paul";
    if (name=="Sewel") return "Sewell";
    if (name=="Sjolie") return "Sjlie";
    if (name=="Sokolosky") return "Sokolowsky";
    if (name=="Sokolsky") return "Sokolowsky";
    if (name=="Sonda") return "Sondra";
    if (name=="Stansilav") return "Stanislav";
    if (name=="Stefan") return "Stephan";
    if (name=="Stell") return "Steel";
    if (name=="Tarantilils") return "Tarantilis";
    if (name=="Wieblen") return "Weiblen";
    if (name=="Xennon") return "Hanson";
    if (name=="Yfei") return "Yefei";
    if (name=="Oritz") return "Ortiz";
    if (name=="Cobin") return "Corbin";
    return name;
}

void nameCorrection(NamePair& name)
{
    if (name.first=="Eric" && name.second=="Hammil")
    {
        name.first = "Chris";
        name.second = "Hanson";
    }
    if (name.first=="Nick" && name.second=="")
    {
        name.first = "Trajce";
        name.second = "Nikolov";
    }
    if (name.first=="Julia" && name.second=="Ortiz")
    {
        name.first = "Julian";
        name.second = "Ortiz";
    }
    if (name.first=="Rune" && name.second=="Schmidt")
    {
        name.first = "Rune";
        name.second = "Schmidt Jensen";
    }
    if (name.first=="Romano" && name.second=="Jos")
    {
        name.first = "Romano";
        name.second = "Jos Magacho da Silva";
    }
    if (name.first=="Rommano" && name.second=="Silva")
    {
        name.first = "Romano";
        name.second = "Jos Magacho da Silva";
    }
}

void lastValidCharacter(const std::string& name, unsigned int& pos,char c)
{
    for(unsigned int i=0;i<pos;++i)
    {
        if (name[i]==c)
        {
            pos = i;
            return;
        }
    }
}

void lastValidCharacter(const std::string& name, unsigned int& last)
{
    lastValidCharacter(name, last, '.');
    lastValidCharacter(name, last, ',');
    lastValidCharacter(name, last, '\'');
    lastValidCharacter(name, last, '/');
    lastValidCharacter(name, last, '\\');
    lastValidCharacter(name, last, ':');
    lastValidCharacter(name, last, ';');
    lastValidCharacter(name, last, ')');
}

NamePair createName(const std::string& first, const std::string& second)
{
    if (first.empty()) return EmptyNamePair;

    unsigned int last = first.size();

    lastValidCharacter(first, last);

    if (last==0) return EmptyNamePair;

    std::string name;

    name.append(first.begin(), first.begin()+last);

    if (!validName(name)) return EmptyNamePair;

    name = typoCorrection(name);

    if (second.empty()) return NamePair(name,"");

    if (!validName(second)) return NamePair(name,"");

    last = second.size();

    lastValidCharacter(second, last);

    if (last>0)
    {
        std::string surname(second.begin(), second.begin()+last);
        surname = typoCorrection(surname);

        return NamePair(name, surname);
    }

    return NamePair(name,"");
}

bool submissionsSequence(const Words& words, unsigned int& i)
{
    if (i+1>=words.size()) return false;

    if (words[i]=="From" ||
        words[i]=="from" ||
        words[i]=="From:" ||
        words[i]=="from:" ||
        words[i]=="Added" ||
        words[i]=="Merged" ||
        words[i]=="Integrated") return true;

    if (i+2>=words.size()) return false;

    if (words[i]=="submitted" && words[i+1]=="by")
    {
        i+=1;
        return true;
    }

    if (words[i]=="Folded" && words[i+1]=="in")
    {
        i+=1;
        return true;
    }

    if (words[i]=="Checked" && words[i+1]=="in")
    {
        i+=1;
        return true;
    }

    if (i+3>=words.size()) return false;

    if (words[i]=="sent" && words[i+1]=="in" && words[i+2]=="by")
    {
        i+=2;
        return true;
    }

    return false;
}

void readContributors(NameSet& names, const std::string& file)
{
    std::ifstream fin(file.c_str());

    Words words;
    while(fin)
    {
        std::string keyword;
        fin >> keyword;
        words.push_back(keyword);
    }

    std::string blank_string;

    for(unsigned int i=0; i< words.size(); ++i)
    {
        if (submissionsSequence(words,i))
        {
            if (i+2<words.size() && validName(words[i+1]))
            {
                NamePair name = createName(words[i+1], words[i+2]);
                nameCorrection(name);
                if (!name.first.empty()) names.insert(name);
                i+=2;
            }
            else if (i+1<words.size() && validName(words[i+1]))
            {
                NamePair name = createName(words[i+1], blank_string);
                nameCorrection(name);
                if (!name.first.empty()) names.insert(name);
                i+=1;
            }
        }
    }

    if (names.size()>1)
    {
        for(NameSet::iterator itr = names.begin();
            itr != names.end();
            )
        {
            if (itr->second.empty())
            {
                NameSet::iterator next_itr = itr;
                ++next_itr;

                if (next_itr!=names.end() && itr->first==next_itr->first)
                {
                    names.erase(itr);
                    itr = next_itr;
                }
                else
                {
                    ++itr;
                }
            }
            else
            {
                ++itr;
            }
        }
    }
}

void buildContributors(NameSet& names)
{
    names.insert(NamePair("Robert","Osfield"));
    names.insert(NamePair("Don","Burns"));
    names.insert(NamePair("Marco","Jez"));
    names.insert(NamePair("Karsten","Weiss"));
    names.insert(NamePair("Graeme","Harkness"));
    names.insert(NamePair("Axel","Volley"));
    names.insert(NamePair("Nikolaus","Hanekamp"));
    names.insert(NamePair("Kristopher","Bixler"));
    names.insert(NamePair("Tanguy","Fautr"));
    names.insert(NamePair("J.E.","Hoffmann"));
}

int main( int argc, char **argv)
{
    osg::ArgumentParser arguments(&argc,argv);
    arguments.getApplicationUsage()->setCommandLineUsage(arguments.getApplicationName()+" [options]");
    arguments.getApplicationUsage()->addCommandLineOption("-h or --help","Display this information");
    arguments.getApplicationUsage()->addCommandLineOption("-c or --contributors","Print out the contributors list.");
    arguments.getApplicationUsage()->addCommandLineOption("-r <file> or --read <file>","Read the changelog to generate an estimated contributors list.");

    std::cout<<osgGetLibraryName()<< " "<< osgGetVersion()<<std::endl<<std::endl;

    bool printContributors = false;
    while ( arguments.read("-c") || arguments.read("--contributors")) printContributors = true;

    std::string changeLog;
    while ( arguments.read("-r",changeLog) || arguments.read("--read",changeLog)) printContributors = true;

    // if user request help write it out to cout.
    if (arguments.read("-h") || arguments.read("--help"))
    {
        std::cout<<arguments.getApplicationUsage()->getCommandLineUsage()<<std::endl;
        arguments.getApplicationUsage()->write(std::cout,arguments.getApplicationUsage()->getCommandLineOptions());
        return 1;
    }

    if (printContributors)
    {
        NameSet names;
        buildContributors(names);
        if (!changeLog.empty())
        {
            readContributors(names, changeLog);
        }

        std::cout<<names.size()<<" Contributors:"<<std::endl<<std::endl;
        for(NameSet::iterator itr = names.begin();
            itr != names.end();
            ++itr)
        {
            std::cout<<"  "<<itr->first<<" "<<itr->second<<std::endl;
        }
    }

    return 0;
}
<|endoftext|>


<commit_before>/***************************Copyright-DO-NOT-REMOVE-THIS-LINE**
 * CONDOR Copyright Notice
 *
 * See LICENSE.TXT for additional notices and disclaimers.
 *
 * Copyright (c)1990-1998 CONDOR Team, Computer Sciences Department,
 * University of Wisconsin-Madison, Madison, WI.  All Rights Reserved.
 * No use of the CONDOR Software Program Source Code is authorized
 * without the express consent of the CONDOR Team.  For more information
 * contact: CONDOR Team, Attention: Professor Miron Livny,
 * 7367 Computer Sciences, 1210 W. Dayton St., Madison, WI 53706-1685,
 * (608) 262-0856 or miron@cs.wisc.edu.
 *
 * U.S. Government Rights Restrictions: Use, duplication, or disclosure
 * by the U.S. Government is subject to restrictions as set forth in
 * subparagraph (c)(1)(ii) of The Rights in Technical Data and Computer
 * Software clause at DFARS 252.227-7013 or subparagraphs (c)(1) and
 * (2) of Commercial Computer Software-Restricted Rights at 48 CFR
 * 52.227-19, as applicable, CONDOR Team, Attention: Professor Miron
 * Livny, 7367 Computer Sciences, 1210 W. Dayton St., Madison,
 * WI 53706-1685, (608) 262-0856 or miron@cs.wisc.edu.
****************************Copyright-DO-NOT-REMOVE-THIS-LINE**/

#include "condor_common.h"
#include "classad.h"

BEGIN_NAMESPACE( classad )

AttributeReference::
AttributeReference()
{
	nodeKind = ATTRREF_NODE;
	expr = NULL;
	absolute = false;
}

// a private ctor for use in significant expr identification
AttributeReference::
AttributeReference( ExprTree *tree, const string &attrname, bool absolut )
{
	nodeKind = ATTRREF_NODE;
	attributeStr = attrname;
	expr = tree;
	absolute = absolut;
}

AttributeReference::
~AttributeReference()
{
	if( expr ) delete expr;
}

AttributeReference *AttributeReference::
Copy( ) const
{
	AttributeReference *newTree = new AttributeReference ();
	if (newTree == 0) {
		CondorErrno = ERR_MEM_ALLOC_FAILED;
		CondorErrMsg = "";
		return NULL;
	}

	newTree->attributeStr = attributeStr;
	if( expr && ( newTree->expr=expr->Copy( ) ) == NULL ) {
		delete newTree;
		return NULL;
	}

	newTree->nodeKind = nodeKind;
	newTree->parentScope = parentScope;
	newTree->absolute = absolute;

	return newTree;
}

void AttributeReference::
_SetParentScope( const ClassAd *parent )
{
	if( expr ) expr->SetParentScope( parent );
}

void AttributeReference::
GetComponents( ExprTree *&tree, string &attr, bool &abs ) const
{
	tree = expr;
	attr = attributeStr;
	abs = absolute;
}

bool AttributeReference::
_Evaluate (EvalState &state, Value &val) const
{
	ExprTree		*tree, *dummy;
	const ClassAd	*curAd;
	bool			rval;

	// find the expression and the evalstate
	curAd = state.curAd;
	switch( FindExpr( state, tree, dummy, false ) ) {
		case EVAL_FAIL:
			return false;

		case EVAL_ERROR:
		case PROP_ERROR:
			val.SetErrorValue();
			state.curAd = curAd;
			return true;

		case EVAL_UNDEF:
		case PROP_UNDEF:
			val.SetUndefinedValue();
			state.curAd = curAd;
			return true;

		case EVAL_OK:
			rval = tree->Evaluate( state , val );
			state.curAd = curAd;
			return rval;

		default:  EXCEPT( "ClassAd:  Should not reach here" );
	}
	return false;
}

bool AttributeReference::
_Evaluate (EvalState &state, Value &val, ExprTree *&sig ) const
{
	ExprTree		*tree, *exprSig;
	const ClassAd	*curAd;
	bool			rval;

	curAd = state.curAd;
	exprSig = NULL;
	rval 	= true;

	switch( FindExpr( state , tree , exprSig , true ) ) {
		case EVAL_FAIL:
			rval = false;
			break;

		case EVAL_ERROR:
		case PROP_ERROR:
			val.SetErrorValue( );
			break;

		case EVAL_UNDEF:
		case PROP_UNDEF:
			val.SetUndefinedValue( );
			break;

		case EVAL_OK:
			rval = tree->Evaluate( state, val );
			break;

		default:  EXCEPT( "ClassAd:  Should not reach here" );
	}
	if(!rval || !(sig=new AttributeReference(exprSig,attributeStr,absolute))){
		if( rval ) {
			CondorErrno = ERR_MEM_ALLOC_FAILED;
			CondorErrMsg = "";
		}
		delete exprSig;
		sig = NULL;
		return( false );
	}
	state.curAd = curAd;
	return rval;
}

bool AttributeReference::
_Flatten( EvalState &state, Value &val, ExprTree*&ntree, OpKind*) const
{
	if( absolute ) {
		ntree = Copy();
		return( ntree != NULL );
	}

	if( !Evaluate( state, val ) ) {
		return false;
	}

	if( val.IsClassAdValue() || val.IsListValue() || val.IsUndefinedValue() ) {
		ntree = Copy();
		return( ntree != NULL );
	} else {
		ntree = NULL;
	}
	return true;
}

int AttributeReference::
FindExpr(EvalState &state, ExprTree *&tree, ExprTree *&sig, bool wantSig) const
{
	const ClassAd 	*current=NULL;
	Value			val;
	bool			rval;

	sig = NULL;

	// establish starting point for search
	if( expr == NULL ) {
		// "attr" and ".attr"
		current = absolute ? state.rootAd : state.curAd;
	} else {
		// "expr.attr"
		rval=wantSig?expr->Evaluate(state,val,sig):expr->Evaluate(state,val);
		if( !rval ) {
			return( EVAL_FAIL );
		}

		if( val.IsUndefinedValue( ) ) {
			return( PROP_UNDEF );
		} else if( val.IsErrorValue( ) ) {
			return( PROP_ERROR );
		}

		if( !val.IsClassAdValue( current ) ) {
			return( EVAL_ERROR );
		}
	}

	// lookup with scope; this may side-affect state
	return( current->LookupInScope( attributeStr, tree, state ) );
}

AttributeReference *AttributeReference::
MakeAttributeReference(ExprTree *tree, const string &attrStr, bool absolut)
{
	return( new AttributeReference( tree, attrStr, absolut ) );
}

END_NAMESPACE // classad
<commit_msg>Fixed flatenning of attribute references<commit_after>/***************************Copyright-DO-NOT-REMOVE-THIS-LINE**
 * CONDOR Copyright Notice
 *
 * See LICENSE.TXT for additional notices and disclaimers.
 *
 * Copyright (c)1990-1998 CONDOR Team, Computer Sciences Department,
 * University of Wisconsin-Madison, Madison, WI.  All Rights Reserved.
 * No use of the CONDOR Software Program Source Code is authorized
 * without the express consent of the CONDOR Team.  For more information
 * contact: CONDOR Team, Attention: Professor Miron Livny,
 * 7367 Computer Sciences, 1210 W. Dayton St., Madison, WI 53706-1685,
 * (608) 262-0856 or miron@cs.wisc.edu.
 *
 * U.S. Government Rights Restrictions: Use, duplication, or disclosure
 * by the U.S. Government is subject to restrictions as set forth in
 * subparagraph (c)(1)(ii) of The Rights in Technical Data and Computer
 * Software clause at DFARS 252.227-7013 or subparagraphs (c)(1) and
 * (2) of Commercial Computer Software-Restricted Rights at 48 CFR
 * 52.227-19, as applicable, CONDOR Team, Attention: Professor Miron
 * Livny, 7367 Computer Sciences, 1210 W. Dayton St., Madison,
 * WI 53706-1685, (608) 262-0856 or miron@cs.wisc.edu.
****************************Copyright-DO-NOT-REMOVE-THIS-LINE**/

#include "condor_common.h"
#include "classad.h"

BEGIN_NAMESPACE( classad )

AttributeReference::
AttributeReference()
{
	nodeKind = ATTRREF_NODE;
	expr = NULL;
	absolute = false;
}

// a private ctor for use in significant expr identification
AttributeReference::
AttributeReference( ExprTree *tree, const string &attrname, bool absolut )
{
	nodeKind = ATTRREF_NODE;
	attributeStr = attrname;
	expr = tree;
	absolute = absolut;
}

AttributeReference::
~AttributeReference()
{
	if( expr ) delete expr;
}

AttributeReference *AttributeReference::
Copy( ) const
{
	AttributeReference *newTree = new AttributeReference ();
	if (newTree == 0) {
		CondorErrno = ERR_MEM_ALLOC_FAILED;
		CondorErrMsg = "";
		return NULL;
	}

	newTree->attributeStr = attributeStr;
	if( expr && ( newTree->expr=expr->Copy( ) ) == NULL ) {
		delete newTree;
		return NULL;
	}

	newTree->nodeKind = nodeKind;
	newTree->parentScope = parentScope;
	newTree->absolute = absolute;

	return newTree;
}

void AttributeReference::
_SetParentScope( const ClassAd *parent )
{
	if( expr ) expr->SetParentScope( parent );
}

void AttributeReference::
GetComponents( ExprTree *&tree, string &attr, bool &abs ) const
{
	tree = expr;
	attr = attributeStr;
	abs = absolute;
}

bool AttributeReference::
_Evaluate (EvalState &state, Value &val) const
{
	ExprTree		*tree, *dummy;
	const ClassAd	*curAd;
	bool			rval;

	// find the expression and the evalstate
	curAd = state.curAd;
	switch( FindExpr( state, tree, dummy, false ) ) {
		case EVAL_FAIL:
			return false;

		case EVAL_ERROR:
		case PROP_ERROR:
			val.SetErrorValue();
			state.curAd = curAd;
			return true;

		case EVAL_UNDEF:
		case PROP_UNDEF:
			val.SetUndefinedValue();
			state.curAd = curAd;
			return true;

		case EVAL_OK:
			rval = tree->Evaluate( state , val );
			state.curAd = curAd;
			return rval;

		default:  EXCEPT( "ClassAd:  Should not reach here" );
	}
	return false;
}

bool AttributeReference::
_Evaluate (EvalState &state, Value &val, ExprTree *&sig ) const
{
	ExprTree		*tree, *exprSig;
	const ClassAd	*curAd;
	bool			rval;

	curAd = state.curAd;
	exprSig = NULL;
	rval 	= true;

	switch( FindExpr( state , tree , exprSig , true ) ) {
		case EVAL_FAIL:
			rval = false;
			break;

		case EVAL_ERROR:
		case PROP_ERROR:
			val.SetErrorValue( );
			break;

		case EVAL_UNDEF:
		case PROP_UNDEF:
			val.SetUndefinedValue( );
			break;

		case EVAL_OK:
			rval = tree->Evaluate( state, val );
			break;

		default:  EXCEPT( "ClassAd:  Should not reach here" );
	}
	if(!rval || !(sig=new AttributeReference(exprSig,attributeStr,absolute))){
		if( rval ) {
			CondorErrno = ERR_MEM_ALLOC_FAILED;
			CondorErrMsg = "";
		}
		delete exprSig;
		sig = NULL;
		return( false );
	}
	state.curAd = curAd;
	return rval;
}

bool AttributeReference::
_Flatten( EvalState &state, Value &val, ExprTree*&ntree, OpKind*) const
{
	ExprTree		*tree, *dummy;
	const ClassAd	*curAd;
	bool			rval;

		// find the expression and the evalstate
	curAd = state.curAd;
	switch( FindExpr( state, tree, dummy, false ) ) {
		case EVAL_FAIL:
			return false;

		case EVAL_ERROR:
		case PROP_ERROR:
			val.SetErrorValue();
			state.curAd = curAd;
			return true;

		case EVAL_UNDEF:
		case PROP_UNDEF:
			if( !(ntree = Copy( ) ) ) {
				CondorErrno = ERR_MEM_ALLOC_FAILED;
				CondorErrMsg = "";
				return( false );
			}
			state.curAd = curAd;
			return true;

		case EVAL_OK:
			rval = tree->Flatten( state, val, ntree );
				// don't inline if it didn't flatten to a value
			if( ntree ) {
				delete ntree;
				ntree = Copy( );
				val.SetUndefinedValue( );
			}
			state.curAd = curAd;
			return rval;

		default:  EXCEPT( "ClassAd:  Should not reach here" );
	}
	return false;
}

int AttributeReference::
FindExpr(EvalState &state, ExprTree *&tree, ExprTree *&sig, bool wantSig) const
{
	const ClassAd 	*current=NULL;
	Value			val;
	bool			rval;

	sig = NULL;

	// establish starting point for search
	if( expr == NULL ) {
		// "attr" and ".attr"
		current = absolute ? state.rootAd : state.curAd;
	} else {
		// "expr.attr"
		rval=wantSig?expr->Evaluate(state,val,sig):expr->Evaluate(state,val);
		if( !rval ) {
			return( EVAL_FAIL );
		}

		if( val.IsUndefinedValue( ) ) {
			return( PROP_UNDEF );
		} else if( val.IsErrorValue( ) ) {
			return( PROP_ERROR );
		}

		if( !val.IsClassAdValue( current ) ) {
			return( EVAL_ERROR );
		}
	}

	// lookup with scope; this may side-affect state
	return( current->LookupInScope( attributeStr, tree, state ) );
}

AttributeReference *AttributeReference::
MakeAttributeReference(ExprTree *tree, const string &attrStr, bool absolut)
{
	return( new AttributeReference( tree, attrStr, absolut ) );
}

END_NAMESPACE // classad
<|endoftext|>


<commit_before>#ifndef CAN_EMULATOR

#include "usbutil.h"
#include "canread.h"
#include "serialutil.h"
#include "signals.h"
#include "log.h"
#include "cJSON.h"
#include "listener.h"
#include <stdint.h>

extern SerialDevice SERIAL_DEVICE;
extern UsbDevice USB_DEVICE;
extern Listener listener;

/* Forward declarations */

void receiveCan(CanBus*);
void initializeAllCan();
bool receiveWriteRequest(uint8_t*);

void setup() {
    initializeLogging();
#ifndef NO_UART
    initializeSerial(&SERIAL_DEVICE);
#endif
    initializeUsb(&USB_DEVICE);
    initializeAllCan();
}

void loop() {
    for(int i = 0; i < getCanBusCount(); i++) {
        receiveCan(&getCanBuses()[i]);
    }
    processListenerQueues(&listener);
    readFromHost(&USB_DEVICE, &receiveWriteRequest);
#ifndef NO_UART
    readFromSerial(&SERIAL_DEVICE, &receiveWriteRequest);
#endif
    for(int i = 0; i < getCanBusCount(); i++) {
        processCanWriteQueue(&getCanBuses()[i]);
    }
}

void initializeAllCan() {
    for(int i = 0; i < getCanBusCount(); i++) {
        initializeCan(&(getCanBuses()[i]));
    }
}

void receiveRawWriteRequest(cJSON* idObject, cJSON* root) {
    uint32_t id = idObject->valueint;
    cJSON* dataObject = cJSON_GetObjectItem(root, "data");
    if(dataObject == NULL) {
        debug("Raw write request missing data\r\n", id);
        return;
    }

    char* dataString = dataObject->valuestring;
    char* end;
    unsigned long long data = __builtin_bswap64(strtoull(dataString, &end, 16));
    CanMessage message = {id, data};
    QUEUE_PUSH(CanMessage, &getCanBuses()[0].sendQueue, message);
}

void receiveBinaryWriteRequest(uint8_t* message) {
    int index = 0;
    const int BINARY_CAN_WRITE_PACKET_LENGTH = 15;

    while(message[index] != '!') {
        if (index + BINARY_CAN_WRITE_PACKET_LENGTH >= 64) {
            debug("!");
        }

        if(message[index] != '{' || message[index+5] != '|'
                || message[index+14] != '}') {
            debug("Received a corrupted CAN message.\r\n");
            for(int i = 0; i < 16; i++) {
                debug("%02x ", message[index+i] );
            }
            debug("\r\n");
        }

        CanMessage outgoing = {0, 0};
        memcpy((uint8_t*)&outgoing.id, &message[index+1], 4);
        for(int i = 0; i < 8; i++) {
            ((uint8_t*)&(outgoing.data))[i] = message[index+i+6];
        }
        QUEUE_PUSH(CanMessage, &getCanBuses()[0].sendQueue, outgoing);
    }
}

void receiveTranslatedWriteRequest(cJSON* nameObject, cJSON* root) {
    char* name = nameObject->valuestring;
    cJSON* value = cJSON_GetObjectItem(root, "value");
    if(value == NULL) {
        debug("Write request for %s missing value\r\n", name);
        return;
    }

    CanSignal* signal = lookupSignal(name, getSignals(),
            getSignalCount(), true);
    CanCommand* command = lookupCommand(name, getCommands(),
            getCommandCount());
    if(signal != NULL) {
        sendCanSignal(signal, value, getSignals(), getSignalCount());
    } else if(command != NULL) {
        command->handler(name, value, getSignals(), getSignalCount());
    } else {
        debug("Writing not allowed for signal with name %s\r\n", name);
    }
}

bool receiveJsonWriteRequest(uint8_t* message) {
    cJSON *root = cJSON_Parse((char*)message);
    bool foundMessage = false;
    if(root != NULL) {
        foundMessage = true;
        cJSON* nameObject = cJSON_GetObjectItem(root, "name");
        if(nameObject == NULL) {
            cJSON* idObject = cJSON_GetObjectItem(root, "id");
            if(idObject == NULL) {
                debug("Write request is malformed, "
                        "missing name or id: %s\r\n", message);
            } else {
                receiveRawWriteRequest(idObject, root);
            }
        } else {
            receiveTranslatedWriteRequest(nameObject, root);
        }
        cJSON_Delete(root);
    } else {
        debug("Unable to parse JSON from \"%s\" -- if it's valid, "
                "may be out of memory\r\n", message);
    }
    return foundMessage;
}

bool receiveWriteRequest(uint8_t* message) {
#ifdef TRANSMITTER
    receiveBinaryWriteRequest(message);
    return true;
#else
    return receiveJsonWriteRequest(message);
#endif
}

/*
 * Check to see if a packet has been received. If so, read the packet and print
 * the packet payload to the serial monitor.
 */
void receiveCan(CanBus* bus) {
    // TODO what happens if we process until the queue is empty?
    if(!QUEUE_EMPTY(CanMessage, &bus->receiveQueue)) {
        CanMessage message = QUEUE_POP(CanMessage, &bus->receiveQueue);
        decodeCanMessage(message.id, message.data);
    }
}

void reset() {
    initializeAllCan();
}

#endif // CAN_EMULATOR
<commit_msg>Some bug fixes, and a comment explaining the expected data format in the CAN message parser.<commit_after>#ifndef CAN_EMULATOR

#include "usbutil.h"
#include "canread.h"
#include "serialutil.h"
#include "signals.h"
#include "log.h"
#include "cJSON.h"
#include "listener.h"
#include <stdint.h>

extern SerialDevice SERIAL_DEVICE;
extern UsbDevice USB_DEVICE;
extern Listener listener;

/* Forward declarations */

void receiveCan(CanBus*);
void initializeAllCan();
bool receiveWriteRequest(uint8_t*);

void setup() {
    initializeLogging();
#ifndef NO_UART
    initializeSerial(&SERIAL_DEVICE);
#endif
    initializeUsb(&USB_DEVICE);
    initializeAllCan();
}

void loop() {
    for(int i = 0; i < getCanBusCount(); i++) {
        receiveCan(&getCanBuses()[i]);
    }
    processListenerQueues(&listener);
    readFromHost(&USB_DEVICE, &receiveWriteRequest);
#ifndef NO_UART
    readFromSerial(&SERIAL_DEVICE, &receiveWriteRequest);
#endif
    for(int i = 0; i < getCanBusCount(); i++) {
        processCanWriteQueue(&getCanBuses()[i]);
    }
}

void initializeAllCan() {
    for(int i = 0; i < getCanBusCount(); i++) {
        initializeCan(&(getCanBuses()[i]));
    }
}

void receiveRawWriteRequest(cJSON* idObject, cJSON* root) {
    uint32_t id = idObject->valueint;
    cJSON* dataObject = cJSON_GetObjectItem(root, "data");
    if(dataObject == NULL) {
        debug("Raw write request missing data\r\n", id);
        return;
    }

    char* dataString = dataObject->valuestring;
    char* end;
    unsigned long long data = __builtin_bswap64(strtoull(dataString, &end, 16));
    CanMessage message = {id, data};
    QUEUE_PUSH(CanMessage, &getCanBuses()[0].sendQueue, message);
}

void receiveBinaryWriteRequest(uint8_t* message) {

  //Expecting a leading '{' followed by a 4 byte message ID,
  // followed by a seperator '|', followed by 8 bytes of data,
  // then a trailing '}'
    int index = 0;
    const int BINARY_CAN_WRITE_PACKET_LENGTH = 15;
    debug(".");
    while((message[index] != '!')  && (index + BINARY_CAN_WRITE_PACKET_LENGTH < 64)) {

	if(message[index] != '{' || message[index+5] != '|'
                || message[index+14] != '}') {
            debug("Received a corrupted CAN message.\r\n");
            for(int i = 0; i < 16; i++) {
                debug("%02x ", message[index+i] );
            }
            debug("\r\n");
	    continue;
        }

        CanMessage outgoing = {0, 0};
        memcpy((uint8_t*)&outgoing.id, &message[index+1], 4);
        for(int i = 0; i < 8; i++) {
            ((uint8_t*)&(outgoing.data))[i] = message[index+i+6];
        }
        QUEUE_PUSH(CanMessage, &getCanBuses()[0].sendQueue, outgoing);
	index += BINARY_CAN_WRITE_PACKET_LENGTH;
    }
}

void receiveTranslatedWriteRequest(cJSON* nameObject, cJSON* root) {
    char* name = nameObject->valuestring;
    cJSON* value = cJSON_GetObjectItem(root, "value");
    if(value == NULL) {
        debug("Write request for %s missing value\r\n", name);
        return;
    }

    CanSignal* signal = lookupSignal(name, getSignals(),
            getSignalCount(), true);
    CanCommand* command = lookupCommand(name, getCommands(),
            getCommandCount());
    if(signal != NULL) {
        sendCanSignal(signal, value, getSignals(), getSignalCount());
    } else if(command != NULL) {
        command->handler(name, value, getSignals(), getSignalCount());
    } else {
        debug("Writing not allowed for signal with name %s\r\n", name);
    }
}

bool receiveJsonWriteRequest(uint8_t* message) {
    cJSON *root = cJSON_Parse((char*)message);
    bool foundMessage = false;
    if(root != NULL) {
        foundMessage = true;
        cJSON* nameObject = cJSON_GetObjectItem(root, "name");
        if(nameObject == NULL) {
            cJSON* idObject = cJSON_GetObjectItem(root, "id");
            if(idObject == NULL) {
                debug("Write request is malformed, "
                        "missing name or id: %s\r\n", message);
            } else {
                receiveRawWriteRequest(idObject, root);
            }
        } else {
            receiveTranslatedWriteRequest(nameObject, root);
        }
        cJSON_Delete(root);
    } else {
        debug("Unable to parse JSON from \"%s\" -- if it's valid, "
                "may be out of memory\r\n", message);
    }
    return foundMessage;
}

bool receiveWriteRequest(uint8_t* message) {
#ifdef TRANSMITTER
    receiveBinaryWriteRequest(message);
    return true;
#else
    return receiveJsonWriteRequest(message);
#endif
}

/*
 * Check to see if a packet has been received. If so, read the packet and print
 * the packet payload to the serial monitor.
 */
void receiveCan(CanBus* bus) {
    // TODO what happens if we process until the queue is empty?
    if(!QUEUE_EMPTY(CanMessage, &bus->receiveQueue)) {
        CanMessage message = QUEUE_POP(CanMessage, &bus->receiveQueue);
        decodeCanMessage(message.id, message.data);
    }
}

void reset() {
    initializeAllCan();
}

#endif // CAN_EMULATOR
<|endoftext|>


<commit_before>/*********************************************************************
 *
 * Condor ClassAd library
 * Copyright (C) 1990-2001, CONDOR Team, Computer Sciences Department,
 * University of Wisconsin-Madison, WI, and Rajesh Raman.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of version 2.1 of the GNU Lesser General
 * Public License as published by the Free Software Foundation.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
 * USA
 *
 *********************************************************************/

#include "common.h"
#include "classad.h"

BEGIN_NAMESPACE( classad )

AttributeReference::
AttributeReference()
{
	nodeKind = ATTRREF_NODE;
	expr = NULL;
	absolute = false;
}

// a private ctor for use in significant expr identification
AttributeReference::
AttributeReference( ExprTree *tree, const string &attrname, bool absolut )
{
	nodeKind = ATTRREF_NODE;
	attributeStr = attrname;
	expr = tree;
	absolute = absolut;
}

AttributeReference::
~AttributeReference()
{
	if( expr ) delete expr;
}

AttributeReference *AttributeReference::
Copy( ) const
{
	AttributeReference *newTree = new AttributeReference ();
	if (newTree == 0) {
		CondorErrno = ERR_MEM_ALLOC_FAILED;
		CondorErrMsg = "";
		return NULL;
	}

	newTree->attributeStr = attributeStr;
	if( expr && ( newTree->expr=expr->Copy( ) ) == NULL ) {
		delete newTree;
		return NULL;
	}

	newTree->nodeKind = nodeKind;
	newTree->parentScope = parentScope;
	newTree->absolute = absolute;

	return newTree;
}

void AttributeReference::
_SetParentScope( const ClassAd *parent )
{
	if( expr ) expr->SetParentScope( parent );
}

void AttributeReference::
GetComponents( ExprTree *&tree, string &attr, bool &abs ) const
{
	tree = expr;
	attr = attributeStr;
	abs = absolute;
}

bool AttributeReference::
_Evaluate (EvalState &state, Value &val) const
{
	ExprTree	*tree;
	ExprTree	*dummy;
	ClassAd		*curAd;
	bool		rval;
	Value		cv;
	EvalCache::iterator	itr;

	// find the expression and the evalstate
	curAd = state.curAd;
	switch( FindExpr( state, tree, dummy, false ) ) {
		case EVAL_FAIL:
			return false;

		case EVAL_ERROR:
		case PROP_ERROR:
			val.SetErrorValue();
			state.curAd = curAd;
			return true;

		case EVAL_UNDEF:
		case PROP_UNDEF:
			val.SetUndefinedValue();
			state.curAd = curAd;
			return true;

		case EVAL_OK:
			// lookup in cache
			itr = state.cache.find( tree );

			// if found, return cached value
			if( itr != state.cache.end( ) ) {
				val.CopyFrom( itr->second );
				state.curAd = curAd;		// NAC (fixed a bug)
				return true;
			}

			// temporarily cache value as undef, so any circular refs
			// in Evaluate() will eval to undef rather than loop

			cv.SetUndefinedValue( );
			state.cache[ tree ] = cv;

			rval = tree->Evaluate( state , val );

			// replace cached undef with actual evaluation
			state.cache[ tree ] = val;

			state.curAd = curAd;

			return rval;

		default:  EXCEPT( "ClassAd:  Should not reach here" );
	}
	return false;
}

bool AttributeReference::
_Evaluate (EvalState &state, Value &val, ExprTree *&sig ) const
{
	ExprTree	*tree;
	ExprTree	*exprSig;
	ClassAd		*curAd;
	bool		rval;
	Value 		cv;
	EvalCache::iterator	itr;

	curAd = state.curAd;
	exprSig = NULL;
	rval 	= true;

	switch( FindExpr( state , tree , exprSig , true ) ) {
		case EVAL_FAIL:
			rval = false;
			break;

		case EVAL_ERROR:
		case PROP_ERROR:
			val.SetErrorValue( );
			break;

		case EVAL_UNDEF:
		case PROP_UNDEF:
			val.SetUndefinedValue( );
			break;

		case EVAL_OK:

			// lookup in cache
			itr = state.cache.find( tree );

			// if found in cache, return cached value
			if( itr != state.cache.end( ) ) {
				val.CopyFrom( itr->second );
				state.curAd = curAd;		// NAC (fixed a bug)
				return true;
			}

			// temporarily cache value as undef, so any circular refs
			// in Evaluate() will eval to undef rather than loop

			cv.SetUndefinedValue( );
			state.cache[ tree ] = cv;

			rval = tree->Evaluate( state, val );

			// replace undef in cache with actual evaluation
			state.cache[ tree ] = val;

			break;

		default:  EXCEPT( "ClassAd:  Should not reach here" );
	}
	if(!rval || !(sig=new AttributeReference(exprSig,attributeStr,absolute))){
		if( rval ) {
			CondorErrno = ERR_MEM_ALLOC_FAILED;
			CondorErrMsg = "";
		}
		delete exprSig;
		sig = NULL;
		return( false );
	}
	state.curAd = curAd;
	return rval;
}

bool AttributeReference::
_Flatten( EvalState &state, Value &val, ExprTree*&ntree, int*) const
{
	ExprTree	*tree;
	ExprTree	*dummy;
	ClassAd		*curAd;
	bool		rval;
	Value		cv;

		// find the expression and the evalstate
	curAd = state.curAd;
	switch( FindExpr( state, tree, dummy, false ) ) {
		case EVAL_FAIL:
			return false;

		case EVAL_ERROR:
		case PROP_ERROR:
			val.SetErrorValue();
			state.curAd = curAd;
			return true;

		case EVAL_UNDEF:
		case PROP_UNDEF:
			if( !(ntree = Copy( ) ) ) {
				CondorErrno = ERR_MEM_ALLOC_FAILED;
				CondorErrMsg = "";
				return( false );
			}
			state.curAd = curAd;
			return true;

		case EVAL_OK:

			// temporarily cache value as undef, so any circular refs
			// in Evaluate() will eval to undef rather than loop

			cv.SetUndefinedValue( );
			state.cache[ tree ] = cv;

			rval = tree->Flatten( state, val, ntree );

			// don't inline if it didn't flatten to a value, and clear cache
			if( ntree ) {
				delete ntree;
				ntree = Copy( );
				val.SetUndefinedValue( );
				state.cache.erase( tree );
			} else {
				state.cache[ tree ] = val;
			}

			state.curAd = curAd;
			return rval;

		default:  EXCEPT( "ClassAd:  Should not reach here" );
	}
	return false;
}

int AttributeReference::
FindExpr(EvalState &state, ExprTree *&tree, ExprTree *&sig, bool wantSig) const
{
	ClassAd	*current=NULL;
	ExprList *adList = NULL;
	Value	val;
	bool	rval;

	sig = NULL;

	// establish starting point for search
	if( expr == NULL ) {
		// "attr" and ".attr"
		current = absolute ? state.rootAd : state.curAd;
	} else {
		// "expr.attr"
		rval=wantSig?expr->Evaluate(state,val,sig):expr->Evaluate(state,val);
		if( !rval ) {
			return( EVAL_FAIL );
		}

		if( val.IsUndefinedValue( ) ) {
			return( PROP_UNDEF );
		} else if( val.IsErrorValue( ) ) {
			return( PROP_ERROR );
		}

		if( !val.IsClassAdValue( current ) && !val.IsListValue( adList ) ) {
			return( EVAL_ERROR );
		}
	}

	if( val.IsClassAdValue( ) ) {
			// lookup with scope; this may side-affect state
		return( current->LookupInScope( attributeStr, tree, state ) );
	}
	else {
		vector< ExprTree *> eVector;
		ExprTree *currExpr = NULL;
		ExprList *nestedList = NULL;
			// iterate through exprList and apply attribute reference
			// to each exprTree
		for(ExprListIterator itr(adList);!itr.IsAfterLast( );itr.NextExpr( )){
			current = NULL;
 			currExpr = itr.CurrentExpr( )->Copy( );
				// establish starting point for search
			if( currExpr == NULL ) {
					// "attr" and ".attr"
				current = absolute ? state.rootAd : state.curAd;
			} else {
					// "expr.attr"

				rval = wantSig ? currExpr->Evaluate( state, val, sig )
					: currExpr->Evaluate( state, val);
				if( !rval ) {
					return( EVAL_FAIL );
				}

				if( val.IsUndefinedValue( ) || val.IsErrorValue( ) ) {
					eVector.push_back( Literal::MakeLiteral( val ) );
					continue;
				}
				else if( !val.IsClassAdValue( current ) ) {
					if( val.IsListValue( nestedList ) ) {
							// recurse the attribute reference on nested lists
						AttributeReference *attrRef = NULL;
						ExprList *evaledList = NULL;
						attrRef = MakeAttributeReference( nestedList,
														  attributeStr,
														  false );
						attrRef->SetParentScope(nestedList->GetParentScope( ));
						val.Clear( );
						rval = wantSig ? attrRef->Evaluate( state, val, sig )
							: attrRef->Evaluate( state, val );
						if( !rval ) {
							return( EVAL_FAIL );
						}
						if( val.IsListValue( evaledList ) ) {
							eVector.push_back( evaledList );
							continue;
						}
					}
					val.SetErrorValue( );
					eVector.push_back( Literal::MakeLiteral( val ) ) ;
					continue;
				}
			}

				// lookup with scope; this may side-affect state
			currExpr = NULL;
			rval = current->LookupInScope( attributeStr, currExpr, state );
			if( currExpr != NULL ) {
				eVector.push_back( currExpr );
			}
			else {
				val.SetUndefinedValue( );
				eVector.push_back( Literal::MakeLiteral( val ) );
			}
		}
		tree = ExprList::MakeExprList( eVector );
		tree->SetParentScope( adList->GetParentScope( ) );
		return EVAL_OK;
	}
}

AttributeReference *AttributeReference::
MakeAttributeReference(ExprTree *tree, const string &attrStr, bool absolut)
{
	return( new AttributeReference( tree, attrStr, absolut ) );
}

END_NAMESPACE // classad
<commit_msg>Rewrote FindExpr to deal with lists in a much simpler way.  In doing so fixed a segfault and some incorrect behavior.<commit_after>/*********************************************************************
 *
 * Condor ClassAd library
 * Copyright (C) 1990-2001, CONDOR Team, Computer Sciences Department,
 * University of Wisconsin-Madison, WI, and Rajesh Raman.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of version 2.1 of the GNU Lesser General
 * Public License as published by the Free Software Foundation.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
 * USA
 *
 *********************************************************************/

#include "common.h"
#include "classad.h"

BEGIN_NAMESPACE( classad )

AttributeReference::
AttributeReference()
{
	nodeKind = ATTRREF_NODE;
	expr = NULL;
	absolute = false;
}

// a private ctor for use in significant expr identification
AttributeReference::
AttributeReference( ExprTree *tree, const string &attrname, bool absolut )
{
	nodeKind = ATTRREF_NODE;
	attributeStr = attrname;
	expr = tree;
	absolute = absolut;
}

AttributeReference::
~AttributeReference()
{
	if( expr ) delete expr;
}

AttributeReference *AttributeReference::
Copy( ) const
{
	AttributeReference *newTree = new AttributeReference ();
	if (newTree == 0) {
		CondorErrno = ERR_MEM_ALLOC_FAILED;
		CondorErrMsg = "";
		return NULL;
	}

	newTree->attributeStr = attributeStr;
	if( expr && ( newTree->expr=expr->Copy( ) ) == NULL ) {
		delete newTree;
		return NULL;
	}

	newTree->nodeKind = nodeKind;
	newTree->parentScope = parentScope;
	newTree->absolute = absolute;

	return newTree;
}

void AttributeReference::
_SetParentScope( const ClassAd *parent )
{
	if( expr ) expr->SetParentScope( parent );
}

void AttributeReference::
GetComponents( ExprTree *&tree, string &attr, bool &abs ) const
{
	tree = expr;
	attr = attributeStr;
	abs = absolute;
}

bool AttributeReference::
_Evaluate (EvalState &state, Value &val) const
{
	ExprTree	*tree;
	ExprTree	*dummy;
	ClassAd		*curAd;
	bool		rval;
	Value		cv;
	EvalCache::iterator	itr;

	// find the expression and the evalstate
	curAd = state.curAd;
	switch( FindExpr( state, tree, dummy, false ) ) {
		case EVAL_FAIL:
			return false;

		case EVAL_ERROR:
		case PROP_ERROR:
			val.SetErrorValue();
			state.curAd = curAd;
			return true;

		case EVAL_UNDEF:
		case PROP_UNDEF:
			val.SetUndefinedValue();
			state.curAd = curAd;
			return true;

		case EVAL_OK:
			// lookup in cache
			itr = state.cache.find( tree );

			// if found, return cached value
			if( itr != state.cache.end( ) ) {
				val.CopyFrom( itr->second );
				state.curAd = curAd;		// NAC (fixed a bug)
				return true;
			}

			// temporarily cache value as undef, so any circular refs
			// in Evaluate() will eval to undef rather than loop

			cv.SetUndefinedValue( );
			state.cache[ tree ] = cv;

			rval = tree->Evaluate( state , val );

			// replace cached undef with actual evaluation
			state.cache[ tree ] = val;

			state.curAd = curAd;

			return rval;

		default:  EXCEPT( "ClassAd:  Should not reach here" );
	}
	return false;
}

bool AttributeReference::
_Evaluate (EvalState &state, Value &val, ExprTree *&sig ) const
{
	ExprTree	*tree;
	ExprTree	*exprSig;
	ClassAd		*curAd;
	bool		rval;
	Value 		cv;
	EvalCache::iterator	itr;

	curAd = state.curAd;
	exprSig = NULL;
	rval 	= true;

	switch( FindExpr( state , tree , exprSig , true ) ) {
		case EVAL_FAIL:
			rval = false;
			break;

		case EVAL_ERROR:
		case PROP_ERROR:
			val.SetErrorValue( );
			break;

		case EVAL_UNDEF:
		case PROP_UNDEF:
			val.SetUndefinedValue( );
			break;

		case EVAL_OK:

			// lookup in cache
			itr = state.cache.find( tree );

			// if found in cache, return cached value
			if( itr != state.cache.end( ) ) {
				val.CopyFrom( itr->second );
				state.curAd = curAd;		// NAC (fixed a bug)
				return true;
			}

			// temporarily cache value as undef, so any circular refs
			// in Evaluate() will eval to undef rather than loop

			cv.SetUndefinedValue( );
			state.cache[ tree ] = cv;

			rval = tree->Evaluate( state, val );

			// replace undef in cache with actual evaluation
			state.cache[ tree ] = val;

			break;

		default:  EXCEPT( "ClassAd:  Should not reach here" );
	}
	if(!rval || !(sig=new AttributeReference(exprSig,attributeStr,absolute))){
		if( rval ) {
			CondorErrno = ERR_MEM_ALLOC_FAILED;
			CondorErrMsg = "";
		}
		delete exprSig;
		sig = NULL;
		return( false );
	}
	state.curAd = curAd;
	return rval;
}

bool AttributeReference::
_Flatten( EvalState &state, Value &val, ExprTree*&ntree, int*) const
{
	ExprTree	*tree;
	ExprTree	*dummy;
	ClassAd		*curAd;
	bool		rval;
	Value		cv;

		// find the expression and the evalstate
	curAd = state.curAd;
	switch( FindExpr( state, tree, dummy, false ) ) {
		case EVAL_FAIL:
			return false;

		case EVAL_ERROR:
		case PROP_ERROR:
			val.SetErrorValue();
			state.curAd = curAd;
			return true;

		case EVAL_UNDEF:
		case PROP_UNDEF:
			if( !(ntree = Copy( ) ) ) {
				CondorErrno = ERR_MEM_ALLOC_FAILED;
				CondorErrMsg = "";
				return( false );
			}
			state.curAd = curAd;
			return true;

		case EVAL_OK:

			// temporarily cache value as undef, so any circular refs
			// in Evaluate() will eval to undef rather than loop

			cv.SetUndefinedValue( );
			state.cache[ tree ] = cv;

			rval = tree->Flatten( state, val, ntree );

			// don't inline if it didn't flatten to a value, and clear cache
			if( ntree ) {
				delete ntree;
				ntree = Copy( );
				val.SetUndefinedValue( );
				state.cache.erase( tree );
			} else {
				state.cache[ tree ] = val;
			}

			state.curAd = curAd;
			return rval;

		default:  EXCEPT( "ClassAd:  Should not reach here" );
	}
	return false;
}

int AttributeReference::
FindExpr(EvalState &state, ExprTree *&tree, ExprTree *&sig, bool wantSig) const
{
	ClassAd	*current=NULL;
	ExprList *adList = NULL;
	Value	val;
	bool	rval;

	sig = NULL;

	// establish starting point for search
	if( expr == NULL ) {
		// "attr" and ".attr"
		current = absolute ? state.rootAd : state.curAd;
	} else {
		// "expr.attr"
		rval=wantSig?expr->Evaluate(state,val,sig):expr->Evaluate(state,val);
		if( !rval ) {
			return( EVAL_FAIL );
		}

		if( val.IsUndefinedValue( ) ) {
			return( PROP_UNDEF );
		} else if( val.IsErrorValue( ) ) {
			return( PROP_ERROR );
		}

		if( !val.IsClassAdValue( current ) && !val.IsListValue( adList ) ) {
			return( EVAL_ERROR );
		}
	}

	if( val.IsListValue( ) ) {
		vector< ExprTree *> eVector;
		ExprTree *currExpr = NULL;
			// iterate through exprList and apply attribute reference
			// to each exprTree
		for(ExprListIterator itr(adList);!itr.IsAfterLast( );itr.NextExpr( )){
 			currExpr = itr.CurrentExpr( )->Copy( );
			if( currExpr == NULL ) {
				return( EVAL_FAIL );
			} else {
				AttributeReference *attrRef = NULL;
				attrRef = MakeAttributeReference( currExpr,
												  attributeStr,
												  false );
				attrRef->SetParentScope( currExpr->GetParentScope( ) );
				val.Clear( );
				rval = wantSig ? attrRef->Evaluate( state, val, sig )
					: attrRef->Evaluate( state, val );
				if( !rval ) {
					return( EVAL_FAIL );
				}
				ClassAd *evaledAd = NULL;
				ExprList *evaledList = NULL;
				if( val.IsClassAdValue( evaledAd ) ) {
					eVector.push_back( evaledAd );
					continue;
				}
				else if( val.IsListValue( evaledList ) ) {
					eVector.push_back( evaledList );
					continue;
				}
				else {
					eVector.push_back( Literal::MakeLiteral( val ) );
				}
			}
		}
		tree = ExprList::MakeExprList( eVector );
		tree->SetParentScope( adList->GetParentScope( ) );
		return EVAL_OK;
	}
		// lookup with scope; this may side-affect state
	return( current->LookupInScope( attributeStr, tree, state ) );
}

AttributeReference *AttributeReference::
MakeAttributeReference(ExprTree *tree, const string &attrStr, bool absolut)
{
	return( new AttributeReference( tree, attrStr, absolut ) );
}

END_NAMESPACE // classad
<|endoftext|>


<commit_before>#include "command.hh"
#include "globals.hh"
#include "eval.hh"
#include "eval-inline.hh"
#include "names.hh"
#include "get-drvs.hh"
#include "common-args.hh"
#include "json.hh"
#include "json-to-value.hh"
#include "shared.hh"

#include <regex>
#include <fstream>

using namespace nix;

std::string wrap(std::string prefix, std::string s)
{
    return prefix + s + ANSI_NORMAL;
}

std::string hilite(const std::string & s, const std::smatch & m, std::string postfix)
{
    return
        m.empty()
        ? s
        : std::string(m.prefix())
          + ANSI_RED + std::string(m.str()) + postfix
          + std::string(m.suffix());
}

struct CmdSearch : SourceExprCommand, MixJSON
{
    std::vector<std::string> res;

    bool writeCache = true;
    bool useCache = true;

    CmdSearch()
    {
        expectArgs("regex", &res);

        mkFlag()
            .longName("update-cache")
            .shortName('u')
            .description("update the package search cache")
            .handler([&]() { writeCache = true; useCache = false; });

        mkFlag()
            .longName("no-cache")
            .description("do not use or update the package search cache")
            .handler([&]() { writeCache = false; useCache = false; });
    }

    std::string name() override
    {
        return "search";
    }

    std::string description() override
    {
        return "query available packages";
    }

    Examples examples() override
    {
        return {
            Example{
                "To show all available packages:",
                "nix search"
            },
            Example{
                "To show any packages containing 'blender' in its name or description:",
                "nix search blender"
            },
            Example{
                "To search for Firefox or Chromium:",
                "nix search 'firefox|chromium'"
            },
            Example{
                "To search for git and frontend or gui:",
                "nix search git 'frontend|gui'"
            }
        };
    }

    void run(ref<Store> store) override
    {
        settings.readOnlyMode = true;

        // Empty search string should match all packages
        // Use "^" here instead of ".*" due to differences in resulting highlighting
        // (see #1893 -- libc++ claims empty search string is not in POSIX grammar)
        if (res.empty()) {
            res.push_back("^");
        }

        std::vector<std::regex> regexes;
        regexes.reserve(res.size());

        for (auto &re : res) {
            regexes.push_back(std::regex(re, std::regex::extended | std::regex::icase));
        }

        auto state = getEvalState();

        auto jsonOut = json ? std::make_unique<JSONObject>(std::cout) : nullptr;

        auto sToplevel = state->symbols.create("_toplevel");
        auto sRecurse = state->symbols.create("recurseForDerivations");

        bool fromCache = false;

        std::map<std::string, std::string> results;

        std::function<void(Value *, std::string, bool, JSONObject *)> doExpr;

        doExpr = [&](Value * v, std::string attrPath, bool toplevel, JSONObject * cache) {
            debug("at attribute '%s'", attrPath);

            try {
                uint found = 0;

                state->forceValue(*v);

                if (v->type == tLambda && toplevel) {
                    Value * v2 = state->allocValue();
                    state->autoCallFunction(*state->allocBindings(1), *v, *v2);
                    v = v2;
                    state->forceValue(*v);
                }

                if (state->isDerivation(*v)) {

                    DrvInfo drv(*state, attrPath, v->attrs);
                    std::string description;
                    std::smatch attrPathMatch;
                    std::smatch descriptionMatch;
                    std::smatch nameMatch;
                    std::string name;

                    DrvName parsed(drv.queryName());

                    for (auto &regex : regexes) {
                        std::regex_search(attrPath, attrPathMatch, regex);

                        name = parsed.name;
                        std::regex_search(name, nameMatch, regex);

                        description = drv.queryMetaString("description");
                        std::replace(description.begin(), description.end(), '\n', ' ');
                        std::regex_search(description, descriptionMatch, regex);

                        if (!attrPathMatch.empty()
                            || !nameMatch.empty()
                            || !descriptionMatch.empty())
                        {
                            found++;
                        }
                    }

                    if (found == res.size()) {
                        if (json) {

                            auto jsonElem = jsonOut->object(attrPath);

                            jsonElem.attr("pkgName", parsed.name);
                            jsonElem.attr("version", parsed.version);
                            jsonElem.attr("description", description);

                        } else {
                            auto name = hilite(parsed.name, nameMatch, "\e[0;2m")
                                + std::string(parsed.fullName, parsed.name.length());
                            results[attrPath] = fmt(
                                "* %s (%s)\n  %s\n",
                                wrap("\e[0;1m", hilite(attrPath, attrPathMatch, "\e[0;1m")),
                                wrap("\e[0;2m", hilite(name, nameMatch, "\e[0;2m")),
                                hilite(description, descriptionMatch, ANSI_NORMAL));
                        }
                    }

                    if (cache) {
                        cache->attr("type", "derivation");
                        cache->attr("name", drv.queryName());
                        cache->attr("system", drv.querySystem());
                        if (description != "") {
                            auto meta(cache->object("meta"));
                            meta.attr("description", description);
                        }
                    }
                }

                else if (v->type == tAttrs) {

                    if (!toplevel) {
                        auto attrs = v->attrs;
                        Bindings::iterator j = attrs->find(sRecurse);
                        if (j == attrs->end() || !state->forceBool(*j->value, *j->pos)) {
                            debug("skip attribute '%s'", attrPath);
                            return;
                        }
                    }

                    bool toplevel2 = false;
                    if (!fromCache) {
                        Bindings::iterator j = v->attrs->find(sToplevel);
                        toplevel2 = j != v->attrs->end() && state->forceBool(*j->value, *j->pos);
                    }

                    for (auto & i : *v->attrs) {
                        auto cache2 =
                            cache ? std::make_unique<JSONObject>(cache->object(i.name)) : nullptr;
                        doExpr(i.value,
                            attrPath == "" ? (std::string) i.name : attrPath + "." + (std::string) i.name,
                            toplevel2 || fromCache, cache2 ? cache2.get() : nullptr);
                    }
                }

            } catch (AssertionError & e) {
            } catch (Error & e) {
                if (!toplevel) {
                    e.addPrefix(fmt("While evaluating the attribute '%s':\n", attrPath));
                    throw;
                }
            }
        };

        Path jsonCacheFileName = getCacheDir() + "/nix/package-search.json";

        if (useCache && pathExists(jsonCacheFileName)) {

            warn("using cached results; pass '-u' to update the cache");

            Value vRoot;
            parseJSON(*state, readFile(jsonCacheFileName), vRoot);

            fromCache = true;

            doExpr(&vRoot, "", true, nullptr);
        }

        else {
            createDirs(dirOf(jsonCacheFileName));

            Path tmpFile = fmt("%s.tmp.%d", jsonCacheFileName, getpid());

            std::ofstream jsonCacheFile;

            try {
                // iostream considered harmful
                jsonCacheFile.exceptions(std::ofstream::failbit);
                jsonCacheFile.open(tmpFile);

                auto cache = writeCache ? std::make_unique<JSONObject>(jsonCacheFile, false) : nullptr;

                doExpr(getSourceExpr(*state), "", true, cache.get());

            } catch (std::exception &) {
                /* Fun fact: catching std::ios::failure does not work
                   due to C++11 ABI shenanigans.
                   https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66145 */
                if (!jsonCacheFile)
                    throw Error("error writing to %s", tmpFile);
            }

            if (writeCache && rename(tmpFile.c_str(), jsonCacheFileName.c_str()) == -1)
                throw SysError("cannot rename '%s' to '%s'", tmpFile, jsonCacheFileName);
        }

        if (results.size() == 0)
            throw Error("no results for the given search term(s)!");

        RunPager pager;
        for (auto el : results) std::cout << el.second << "\n";

    }
};

static RegisterCommand r1(make_ref<CmdSearch>());
<commit_msg>nix search: Don't quietly ignore errors<commit_after>#include "command.hh"
#include "globals.hh"
#include "eval.hh"
#include "eval-inline.hh"
#include "names.hh"
#include "get-drvs.hh"
#include "common-args.hh"
#include "json.hh"
#include "json-to-value.hh"
#include "shared.hh"

#include <regex>
#include <fstream>

using namespace nix;

std::string wrap(std::string prefix, std::string s)
{
    return prefix + s + ANSI_NORMAL;
}

std::string hilite(const std::string & s, const std::smatch & m, std::string postfix)
{
    return
        m.empty()
        ? s
        : std::string(m.prefix())
          + ANSI_RED + std::string(m.str()) + postfix
          + std::string(m.suffix());
}

struct CmdSearch : SourceExprCommand, MixJSON
{
    std::vector<std::string> res;

    bool writeCache = true;
    bool useCache = true;

    CmdSearch()
    {
        expectArgs("regex", &res);

        mkFlag()
            .longName("update-cache")
            .shortName('u')
            .description("update the package search cache")
            .handler([&]() { writeCache = true; useCache = false; });

        mkFlag()
            .longName("no-cache")
            .description("do not use or update the package search cache")
            .handler([&]() { writeCache = false; useCache = false; });
    }

    std::string name() override
    {
        return "search";
    }

    std::string description() override
    {
        return "query available packages";
    }

    Examples examples() override
    {
        return {
            Example{
                "To show all available packages:",
                "nix search"
            },
            Example{
                "To show any packages containing 'blender' in its name or description:",
                "nix search blender"
            },
            Example{
                "To search for Firefox or Chromium:",
                "nix search 'firefox|chromium'"
            },
            Example{
                "To search for git and frontend or gui:",
                "nix search git 'frontend|gui'"
            }
        };
    }

    void run(ref<Store> store) override
    {
        settings.readOnlyMode = true;

        // Empty search string should match all packages
        // Use "^" here instead of ".*" due to differences in resulting highlighting
        // (see #1893 -- libc++ claims empty search string is not in POSIX grammar)
        if (res.empty()) {
            res.push_back("^");
        }

        std::vector<std::regex> regexes;
        regexes.reserve(res.size());

        for (auto &re : res) {
            regexes.push_back(std::regex(re, std::regex::extended | std::regex::icase));
        }

        auto state = getEvalState();

        auto jsonOut = json ? std::make_unique<JSONObject>(std::cout) : nullptr;

        auto sToplevel = state->symbols.create("_toplevel");
        auto sRecurse = state->symbols.create("recurseForDerivations");

        bool fromCache = false;

        std::map<std::string, std::string> results;

        std::function<void(Value *, std::string, bool, JSONObject *)> doExpr;

        doExpr = [&](Value * v, std::string attrPath, bool toplevel, JSONObject * cache) {
            debug("at attribute '%s'", attrPath);

            try {
                uint found = 0;

                state->forceValue(*v);

                if (v->type == tLambda && toplevel) {
                    Value * v2 = state->allocValue();
                    state->autoCallFunction(*state->allocBindings(1), *v, *v2);
                    v = v2;
                    state->forceValue(*v);
                }

                if (state->isDerivation(*v)) {

                    DrvInfo drv(*state, attrPath, v->attrs);
                    std::string description;
                    std::smatch attrPathMatch;
                    std::smatch descriptionMatch;
                    std::smatch nameMatch;
                    std::string name;

                    DrvName parsed(drv.queryName());

                    for (auto &regex : regexes) {
                        std::regex_search(attrPath, attrPathMatch, regex);

                        name = parsed.name;
                        std::regex_search(name, nameMatch, regex);

                        description = drv.queryMetaString("description");
                        std::replace(description.begin(), description.end(), '\n', ' ');
                        std::regex_search(description, descriptionMatch, regex);

                        if (!attrPathMatch.empty()
                            || !nameMatch.empty()
                            || !descriptionMatch.empty())
                        {
                            found++;
                        }
                    }

                    if (found == res.size()) {
                        if (json) {

                            auto jsonElem = jsonOut->object(attrPath);

                            jsonElem.attr("pkgName", parsed.name);
                            jsonElem.attr("version", parsed.version);
                            jsonElem.attr("description", description);

                        } else {
                            auto name = hilite(parsed.name, nameMatch, "\e[0;2m")
                                + std::string(parsed.fullName, parsed.name.length());
                            results[attrPath] = fmt(
                                "* %s (%s)\n  %s\n",
                                wrap("\e[0;1m", hilite(attrPath, attrPathMatch, "\e[0;1m")),
                                wrap("\e[0;2m", hilite(name, nameMatch, "\e[0;2m")),
                                hilite(description, descriptionMatch, ANSI_NORMAL));
                        }
                    }

                    if (cache) {
                        cache->attr("type", "derivation");
                        cache->attr("name", drv.queryName());
                        cache->attr("system", drv.querySystem());
                        if (description != "") {
                            auto meta(cache->object("meta"));
                            meta.attr("description", description);
                        }
                    }
                }

                else if (v->type == tAttrs) {

                    if (!toplevel) {
                        auto attrs = v->attrs;
                        Bindings::iterator j = attrs->find(sRecurse);
                        if (j == attrs->end() || !state->forceBool(*j->value, *j->pos)) {
                            debug("skip attribute '%s'", attrPath);
                            return;
                        }
                    }

                    bool toplevel2 = false;
                    if (!fromCache) {
                        Bindings::iterator j = v->attrs->find(sToplevel);
                        toplevel2 = j != v->attrs->end() && state->forceBool(*j->value, *j->pos);
                    }

                    for (auto & i : *v->attrs) {
                        auto cache2 =
                            cache ? std::make_unique<JSONObject>(cache->object(i.name)) : nullptr;
                        doExpr(i.value,
                            attrPath == "" ? (std::string) i.name : attrPath + "." + (std::string) i.name,
                            toplevel2 || fromCache, cache2 ? cache2.get() : nullptr);
                    }
                }

            } catch (AssertionError & e) {
            } catch (Error & e) {
                if (!toplevel) {
                    e.addPrefix(fmt("While evaluating the attribute '%s':\n", attrPath));
                    throw;
                }
            }
        };

        Path jsonCacheFileName = getCacheDir() + "/nix/package-search.json";

        if (useCache && pathExists(jsonCacheFileName)) {

            warn("using cached results; pass '-u' to update the cache");

            Value vRoot;
            parseJSON(*state, readFile(jsonCacheFileName), vRoot);

            fromCache = true;

            doExpr(&vRoot, "", true, nullptr);
        }

        else {
            createDirs(dirOf(jsonCacheFileName));

            Path tmpFile = fmt("%s.tmp.%d", jsonCacheFileName, getpid());

            std::ofstream jsonCacheFile;

            try {
                // iostream considered harmful
                jsonCacheFile.exceptions(std::ofstream::failbit);
                jsonCacheFile.open(tmpFile);

                auto cache = writeCache ? std::make_unique<JSONObject>(jsonCacheFile, false) : nullptr;

                doExpr(getSourceExpr(*state), "", true, cache.get());

            } catch (std::exception &) {
                /* Fun fact: catching std::ios::failure does not work
                   due to C++11 ABI shenanigans.
                   https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66145 */
                if (!jsonCacheFile)
                    throw Error("error writing to %s", tmpFile);
                throw;
            }

            if (writeCache && rename(tmpFile.c_str(), jsonCacheFileName.c_str()) == -1)
                throw SysError("cannot rename '%s' to '%s'", tmpFile, jsonCacheFileName);
        }

        if (results.size() == 0)
            throw Error("no results for the given search term(s)!");

        RunPager pager;
        for (auto el : results) std::cout << el.second << "\n";

    }
};

static RegisterCommand r1(make_ref<CmdSearch>());
<|endoftext|>