/** * Simple build automation tool. * * Authors: * Bob Jamison * * Copyright (C) 2006 Bob Jamison * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /** * To use this file, compile with: *
 * g++ -O3 buildtool.cpp -o build.exe
 * (or whatever your compiler might be) 
 * Then
 * build
 * or 
 * build {target}
 */  



#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 

#ifdef __WIN32__
#include 
#endif



namespace buildtool
{






//########################################################################
//########################################################################
//##  X M L
//########################################################################
//########################################################################

// Note:  This mini-dom library comes from Pedro, another little project
// of mine.

typedef std::string String;
typedef unsigned int XMLCh;


class Namespace
{
public:
    Namespace()
        {}

    Namespace(const String &prefixArg, const String &namespaceURIArg)
        {
        prefix       = prefixArg;
        namespaceURI = namespaceURIArg;
        }

    Namespace(const Namespace &other)
        {
        assign(other);
        }

    Namespace &operator=(const Namespace &other)
        {
        assign(other);
        return *this;
        }

    virtual ~Namespace()
        {}

    virtual String getPrefix()
        { return prefix; }

    virtual String getNamespaceURI()
        { return namespaceURI; }

protected:

    void assign(const Namespace &other)
        {
        prefix       = other.prefix;
        namespaceURI = other.namespaceURI;
        }

    String prefix;
    String namespaceURI;

};

class Attribute
{
public:
    Attribute()
        {}

    Attribute(const String &nameArg, const String &valueArg)
        {
        name  = nameArg;
        value = valueArg;
        }

    Attribute(const Attribute &other)
        {
        assign(other);
        }

    Attribute &operator=(const Attribute &other)
        {
        assign(other);
        return *this;
        }

    virtual ~Attribute()
        {}

    virtual String getName()
        { return name; }

    virtual String getValue()
        { return value; }

protected:

    void assign(const Attribute &other)
        {
        name  = other.name;
        value = other.value;
        }

    String name;
    String value;

};


class Element
{
friend class Parser;

public:
    Element()
        {
        parent = NULL;
        }

    Element(const String &nameArg)
        {
        parent = NULL;
        name   = nameArg;
        }

    Element(const String &nameArg, const String &valueArg)
        {
        parent = NULL;
        name   = nameArg;
        value  = valueArg;
        }

    Element(const Element &other)
        {
        assign(other);
        }

    Element &operator=(const Element &other)
        {
        assign(other);
        return *this;
        }

    virtual Element *clone();

    virtual ~Element()
        {
        for (unsigned int i=0 ; i getChildren()
        { return children; }

    std::vector findElements(const String &name);

    String getAttribute(const String &name);

    std::vector &getAttributes()
        { return attributes; } 

    String getTagAttribute(const String &tagName, const String &attrName);

    String getTagValue(const String &tagName);

    void addChild(Element *child);

    void addAttribute(const String &name, const String &value);

    void addNamespace(const String &prefix, const String &namespaceURI);


    /**
     * Prettyprint an XML tree to an output stream.  Elements are indented
     * according to element hierarchy.
     * @param f a stream to receive the output
     * @param elem the element to output
     */
    void writeIndented(FILE *f);

    /**
     * Prettyprint an XML tree to standard output.  This is the equivalent of
     * writeIndented(stdout).
     * @param elem the element to output
     */
    void print();

protected:

    void assign(const Element &other)
        {
        parent     = other.parent;
        children   = other.children;
        attributes = other.attributes;
        namespaces = other.namespaces;
        name       = other.name;
        value      = other.value;
        }

    void findElementsRecursive(std::vector&res, const String &name);

    void writeIndentedRecursive(FILE *f, int indent);

    Element *parent;

    std::vectorchildren;

    std::vector attributes;
    std::vector namespaces;

    String name;
    String value;

};





class Parser
{
public:
    /**
     * Constructor
     */
    Parser()
        { init(); }

    virtual ~Parser()
        {}

    /**
     * Parse XML in a char buffer.
     * @param buf a character buffer to parse
     * @param pos position to start parsing
     * @param len number of chars, from pos, to parse.
     * @return a pointer to the root of the XML document;
     */
    Element *parse(const char *buf,int pos,int len);

    /**
     * Parse XML in a char buffer.
     * @param buf a character buffer to parse
     * @param pos position to start parsing
     * @param len number of chars, from pos, to parse.
     * @return a pointer to the root of the XML document;
     */
    Element *parse(const String &buf);

    /**
     * Parse a named XML file.  The file is loaded like a data file;
     * the original format is not preserved.
     * @param fileName the name of the file to read
     * @return a pointer to the root of the XML document;
     */
    Element *parseFile(const String &fileName);

    /**
     * Utility method to preprocess a string for XML
     * output, escaping its entities.
     * @param str the string to encode
     */
    static String encode(const String &str);

    /**
     *  Removes whitespace from beginning and end of a string
     */
    String trim(const String &s);

private:

    void init()
        {
        keepGoing       = true;
        currentNode     = NULL;
        parselen        = 0;
        parsebuf        = NULL;
        currentPosition = 0;
        }

    void getLineAndColumn(long pos, long *lineNr, long *colNr);

    void error(char *fmt, ...);

    int peek(long pos);

    int match(long pos, const char *text);

    int skipwhite(long p);

    int getWord(int p0, String &buf);

    int getQuoted(int p0, String &buf, int do_i_parse);

    int parseVersion(int p0);

    int parseDoctype(int p0);

    int parseElement(int p0, Element *par,int depth);

    Element *parse(XMLCh *buf,int pos,int len);

    bool       keepGoing;
    Element    *currentNode;
    long       parselen;
    XMLCh      *parsebuf;
    String  cdatabuf;
    long       currentPosition;
    int        colNr;

};




//########################################################################
//# E L E M E N T
//########################################################################

Element *Element::clone()
{
    Element *elem = new Element(name, value);
    elem->parent     = parent;
    elem->attributes = attributes;
    elem->namespaces = namespaces;

    std::vector::iterator iter;
    for (iter = children.begin(); iter != children.end() ; iter++)
        {
        elem->addChild((*iter)->clone());
        }
    return elem;
}


void Element::findElementsRecursive(std::vector&res, const String &name)
{
    if (getName() == name)
        {
        res.push_back(this);
        }
    for (unsigned int i=0; ifindElementsRecursive(res, name);
}

std::vector Element::findElements(const String &name)
{
    std::vector res;
    findElementsRecursive(res, name);
    return res;
}

String Element::getAttribute(const String &name)
{
    for (unsigned int i=0 ; ielems = findElements(tagName);
    if (elems.size() <1)
        return "";
    String res = elems[0]->getAttribute(attrName);
    return res;
}

String Element::getTagValue(const String &tagName)
{
    std::vectorelems = findElements(tagName);
    if (elems.size() <1)
        return "";
    String res = elems[0]->getValue();
    return res;
}

void Element::addChild(Element *child)
{
    if (!child)
        return;
    child->parent = this;
    children.push_back(child);
}


void Element::addAttribute(const String &name, const String &value)
{
    Attribute attr(name, value);
    attributes.push_back(attr);
}

void Element::addNamespace(const String &prefix, const String &namespaceURI)
{
    Namespace ns(prefix, namespaceURI);
    namespaces.push_back(ns);
}

void Element::writeIndentedRecursive(FILE *f, int indent)
{
    int i;
    if (!f)
        return;
    //Opening tag, and attributes
    for (i=0;i\n");

    //Between the tags
    if (value.size() > 0)
        {
        for (int i=0;iwriteIndentedRecursive(f, indent+2);

    //Closing tag
    for (int i=0; i\n", name.c_str());
}

void Element::writeIndented(FILE *f)
{
    writeIndentedRecursive(f, 0);
}

void Element::print()
{
    writeIndented(stdout);
}


//########################################################################
//# P A R S E R
//########################################################################



typedef struct
    {
    char *escaped;
    char value;
    } EntityEntry;

static EntityEntry entities[] =
{
    { "&" , '&'  },
    { "<"  , '<'  },
    { ">"  , '>'  },
    { "'", '\'' },
    { """, '"'  },
    { NULL    , '\0' }
};



/**
 *  Removes whitespace from beginning and end of a string
 */
String Parser::trim(const String &s)
{
    if (s.size() < 1)
        return s;
    
    //Find first non-ws char
    unsigned int begin = 0;
    for ( ; begin < s.size() ; begin++)
        {
        if (!isspace(s[begin]))
            break;
        }

    //Find first non-ws char, going in reverse
    unsigned int end = s.size() - 1;
    for ( ; end > begin ; end--)
        {
        if (!isspace(s[end]))
            break;
        }
    //trace("begin:%d  end:%d", begin, end);

    String res = s.substr(begin, end-begin+1);
    return res;
}

void Parser::getLineAndColumn(long pos, long *lineNr, long *colNr)
{
    long line = 1;
    long col  = 1;
    for (long i=0 ; i= parselen)
        return -1;
    currentPosition = pos;
    int ch = parsebuf[pos];
    //printf("ch:%c\n", ch);
    return ch;
}



String Parser::encode(const String &str)
{
    String ret;
    for (unsigned int i=0 ; i')
            ret.append(">");
        else if (ch == '\'')
            ret.append("'");
        else if (ch == '"')
            ret.append(""");
        else
            ret.push_back(ch);

        }
    return ret;
}


int Parser::match(long p0, const char *text)
{
    int p = p0;
    while (*text)
        {
        if (peek(p) != *text)
            return p0;
        p++; text++;
        }
    return p;
}



int Parser::skipwhite(long p)
{

    while (p p)
            {
            p = p2;
            while (p");
              if (p2 > p)
                  {
                  p = p2;
                  break;
                  }
              p++;
              }
          }
      XMLCh b = peek(p);
      if (!isspace(b))
          break;
      p++;
      }
  return p;
}

/* modify this to allow all chars for an element or attribute name*/
int Parser::getWord(int p0, String &buf)
{
    int p = p0;
    while (p' || b=='=')
            break;
        buf.push_back(b);
        p++;
        }
    return p;
}

int Parser::getQuoted(int p0, String &buf, int do_i_parse)
{

    int p = p0;
    if (peek(p) != '"' && peek(p) != '\'')
        return p0;
    p++;

    while ( pvalue ; ee++)
                {
                int p2 = match(p, ee->escaped);
                if (p2>p)
                    {
                    buf.push_back(ee->value);
                    p = p2;
                    found = true;
                    break;
                    }
                }
            if (!found)
                {
                error("unterminated entity");
                return false;
                }
            }
        else
            {
            buf.push_back(b);
            p++;
            }
        }
    return p;
}

int Parser::parseVersion(int p0)
{
    //printf("### parseVersion: %d\n", p0);

    int p = p0;

    p = skipwhite(p0);

    if (peek(p) != '<')
        return p0;

    p++;
    if (p>=parselen || peek(p)!='?')
        return p0;

    p++;

    String buf;

    while (p=parselen || peek(p)!='<')
        return p0;

    p++;

    if (peek(p)!='!' || peek(p+1)=='-')
        return p0;
    p++;

    String buf;
    while (p')
            {
            p++;
            break;
            }
        buf.push_back(ch);
        p++;
        }

    //printf("Got doctype:%s\n",buf.c_str());
    return p;
}

int Parser::parseElement(int p0, Element *par,int depth)
{

    int p = p0;

    int p2 = p;

    p = skipwhite(p);

    //## Get open tag
    XMLCh ch = peek(p);
    if (ch!='<')
        return p0;

    p++;

    String openTagName;
    p = skipwhite(p);
    p = getWord(p, openTagName);
    //printf("####tag :%s\n", openTagName.c_str());
    p = skipwhite(p);

    //Add element to tree
    Element *n = new Element(openTagName);
    n->parent = par;
    par->addChild(n);

    // Get attributes
    if (peek(p) != '>')
        {
        while (p')
                break;
            else if (ch=='/' && p')
                    {
                    p++;
                    //printf("quick close\n");
                    return p;
                    }
                }
            String attrName;
            p2 = getWord(p, attrName);
            if (p2==p)
                break;
            //printf("name:%s",buf);
            p=p2;
            p = skipwhite(p);
            ch = peek(p);
            //printf("ch:%c\n",ch);
            if (ch!='=')
                break;
            p++;
            p = skipwhite(p);
            // ch = parsebuf[p];
            // printf("ch:%c\n",ch);
            String attrVal;
            p2 = getQuoted(p, attrVal, true);
            p=p2+1;
            //printf("name:'%s'   value:'%s'\n",attrName.c_str(),attrVal.c_str());
            char *namestr = (char *)attrName.c_str();
            if (strncmp(namestr, "xmlns:", 6)==0)
                n->addNamespace(attrName, attrVal);
            else
                n->addAttribute(attrName, attrVal);
            }
        }

    bool cdata = false;

    p++;
    // ### Get intervening data ### */
    String data;
    while (pp)
            {
            p = p2;
            while (p");
                if (p2 > p)
                    {
                    p = p2;
                    break;
                    }
                p++;
                }
            }

        ch = peek(p);
        //# END TAG
        if (ch=='<' && !cdata && peek(p+1)=='/')
            {
            break;
            }
        //# CDATA
        p2 = match(p, " p)
            {
            cdata = true;
            p = p2;
            continue;
            }

        //# CHILD ELEMENT
        if (ch == '<')
            {
            p2 = parseElement(p, n, depth+1);
            if (p2 == p)
                {
                /*
                printf("problem on element:%s.  p2:%d p:%d\n",
                      openTagName.c_str(), p2, p);
                */
                return p0;
                }
            p = p2;
            continue;
            }
        //# ENTITY
        if (ch=='&' && !cdata)
            {
            bool found = false;
            for (EntityEntry *ee = entities ; ee->value ; ee++)
                {
                int p2 = match(p, ee->escaped);
                if (p2>p)
                    {
                    data.push_back(ee->value);
                    p = p2;
                    found = true;
                    break;
                    }
                }
            if (!found)
                {
                error("unterminated entity");
                return -1;
                }
            continue;
            }

        //# NONE OF THE ABOVE
        data.push_back(ch);
        p++;
        }/*while*/


    n->value = data;
    //printf("%d : data:%s\n",p,data.c_str());

    //## Get close tag
    p = skipwhite(p);
    ch = peek(p);
    if (ch != '<')
        {
        error("no < for end tag\n");
        return p0;
        }
    p++;
    ch = peek(p);
    if (ch != '/')
        {
        error("no / on end tag");
        return p0;
        }
    p++;
    ch = peek(p);
    p = skipwhite(p);
    String closeTagName;
    p = getWord(p, closeTagName);
    if (openTagName != closeTagName)
        {
        error("Mismatched closing tag.  Expected . Got '%S'.",
                openTagName.c_str(), closeTagName.c_str());
        return p0;
        }
    p = skipwhite(p);
    if (peek(p) != '>')
        {
        error("no > on end tag for '%s'", closeTagName.c_str());
        return p0;
        }
    p++;
    // printf("close element:%s\n",closeTagName.c_str());
    p = skipwhite(p);
    return p;
}




Element *Parser::parse(XMLCh *buf,int pos,int len)
{
    parselen = len;
    parsebuf = buf;
    Element *rootNode = new Element("root");
    pos = parseVersion(pos);
    pos = parseDoctype(pos);
    pos = parseElement(pos, rootNode, 0);
    return rootNode;
}


Element *Parser::parse(const char *buf, int pos, int len)
{
    XMLCh *charbuf = new XMLCh[len + 1];
    long i = 0;
    for ( ; i < len ; i++)
        charbuf[i] = (XMLCh)buf[i];
    charbuf[i] = '\0';

    Element *n = parse(charbuf, pos, len);
    delete[] charbuf;
    return n;
}

Element *Parser::parse(const String &buf)
{
    long len = (long)buf.size();
    XMLCh *charbuf = new XMLCh[len + 1];
    long i = 0;
    for ( ; i < len ; i++)
        charbuf[i] = (XMLCh)buf[i];
    charbuf[i] = '\0';

    Element *n = parse(charbuf, 0, len);
    delete[] charbuf;
    return n;
}

Element *Parser::parseFile(const String &fileName)
{

    //##### LOAD INTO A CHAR BUF, THEN CONVERT TO XMLCh
    FILE *f = fopen(fileName.c_str(), "rb");
    if (!f)
        return NULL;

    struct stat  statBuf;
    if (fstat(fileno(f),&statBuf)<0)
        {
        fclose(f);
        return NULL;
        }
    long filelen = statBuf.st_size;

    //printf("length:%d\n",filelen);
    XMLCh *charbuf = new XMLCh[filelen + 1];
    for (XMLCh *p=charbuf ; !feof(f) ; p++)
        {
        *p = (XMLCh)fgetc(f);
        }
    fclose(f);
    charbuf[filelen] = '\0';


    /*
    printf("nrbytes:%d\n",wc_count);
    printf("buf:%ls\n======\n",charbuf);
    */
    Element *n = parse(charbuf, 0, filelen);
    delete[] charbuf;
    return n;
}



//########################################################################
//########################################################################
//##  E N D    X M L
//########################################################################
//########################################################################


//########################################################################
//########################################################################
//##  U R I
//########################################################################
//########################################################################

//This would normally be a call to a UNICODE function
#define isLetter(x) isalpha(x)

/**
 *  A class that implements the W3C URI resource reference.
 */
class URI
{
public:

    typedef enum
        {
        SCHEME_NONE =0,
        SCHEME_DATA,
        SCHEME_HTTP,
        SCHEME_HTTPS,
        SCHEME_FTP,
        SCHEME_FILE,
        SCHEME_LDAP,
        SCHEME_MAILTO,
        SCHEME_NEWS,
        SCHEME_TELNET
        } SchemeTypes;

    /**
     *
     */
    URI()
        {
        init();
        }

    /**
     *
     */
    URI(const String &str)
        {
        init();
        parse(str);
        }


    /**
     *
     */
    URI(const char *str)
        {
        init();
        String domStr = str;
        parse(domStr);
        }


    /**
     *
     */
    URI(const URI &other)
        {
        init();
        assign(other);
        }


    /**
     *
     */
    URI &operator=(const URI &other)
        {
        init();
        assign(other);
        return *this;
        }


    /**
     *
     */
    virtual ~URI()
        {}



    /**
     *
     */
    virtual bool parse(const String &str);

    /**
     *
     */
    virtual String toString() const;

    /**
     *
     */
    virtual int getScheme() const;

    /**
     *
     */
    virtual String getSchemeStr() const;

    /**
     *
     */
    virtual String getAuthority() const;

    /**
     *  Same as getAuthority, but if the port has been specified
     *  as host:port , the port will not be included
     */
    virtual String getHost() const;

    /**
     *
     */
    virtual int getPort() const;

    /**
     *
     */
    virtual String getPath() const;

    /**
     *
     */
    virtual String getNativePath() const;

    /**
     *
     */
    virtual bool isAbsolute() const;

    /**
     *
     */
    virtual bool isOpaque() const;

    /**
     *
     */
    virtual String getQuery() const;

    /**
     *
     */
    virtual String getFragment() const;

    /**
     *
     */
    virtual URI resolve(const URI &other) const;

    /**
     *
     */
    virtual void normalize();

private:

    /**
     *
     */
    void init()
        {
        parsebuf  = NULL;
        parselen  = 0;
        scheme    = SCHEME_NONE;
        schemeStr = "";
        port      = 0;
        authority = "";
        path      = "";
        absolute  = false;
        opaque    = false;
        query     = "";
        fragment  = "";
        }


    /**
     *
     */
    void assign(const URI &other)
        {
        scheme    = other.scheme;
        schemeStr = other.schemeStr;
        authority = other.authority;
        port      = other.port;
        path      = other.path;
        absolute  = other.absolute;
        opaque    = other.opaque;
        query     = other.query;
        fragment  = other.fragment;
        }

    int scheme;

    String schemeStr;

    String authority;

    bool portSpecified;

    int port;

    String path;

    bool absolute;

    bool opaque;

    String query;

    String fragment;

    void error(const char *fmt, ...);

    void trace(const char *fmt, ...);


    int peek(int p);

    int match(int p, char *key);

    int parseScheme(int p);

    int parseHierarchicalPart(int p0);

    int parseQuery(int p0);

    int parseFragment(int p0);

    int parse(int p);

    char *parsebuf;

    int parselen;

};



typedef struct
{
    int  ival;
    char *sval;
    int  port;
} LookupEntry;

LookupEntry schemes[] =
{
    { URI::SCHEME_DATA,   "data:",    0 },
    { URI::SCHEME_HTTP,   "http:",   80 },
    { URI::SCHEME_HTTPS,  "https:", 443 },
    { URI::SCHEME_FTP,    "ftp",     12 },
    { URI::SCHEME_FILE,   "file:",    0 },
    { URI::SCHEME_LDAP,   "ldap:",  123 },
    { URI::SCHEME_MAILTO, "mailto:", 25 },
    { URI::SCHEME_NEWS,   "news:",  117 },
    { URI::SCHEME_TELNET, "telnet:", 23 },
    { 0,                  NULL,       0 }
};


String URI::toString() const
{
    String str = schemeStr;
    if (authority.size() > 0)
        {
        str.append("//");
        str.append(authority);
        }
    str.append(path);
    if (query.size() > 0)
        {
        str.append("?");
        str.append(query);
        }
    if (fragment.size() > 0)
        {
        str.append("#");
        str.append(fragment);
        }
    return str;
}


int URI::getScheme() const
{
    return scheme;
}

String URI::getSchemeStr() const
{
    return schemeStr;
}


String URI::getAuthority() const
{
    String ret = authority;
    if (portSpecified && port>=0)
        {
        char buf[7];
        snprintf(buf, 6, ":%6d", port);
        ret.append(buf);
        }
    return ret;
}

String URI::getHost() const
{
    return authority;
}

int URI::getPort() const
{
    return port;
}


String URI::getPath() const
{
    return path;
}

String URI::getNativePath() const
{
    String npath;
#ifdef __WIN32__
    unsigned int firstChar = 0;
    if (path.size() >= 3)
        {
        if (path[0] == '/' &&
            isLetter(path[1]) &&
            path[2] == ':')
            firstChar++;
         }
    for (unsigned int i=firstChar ; i  0 &&
        other.path.size()      == 0 &&
        other.scheme           == SCHEME_NONE &&
        other.authority.size() == 0 &&
        other.query.size()     == 0 )
        {
        URI fragUri = *this;
        fragUri.fragment = other.fragment;
        return fragUri;
        }

    //## 3 http://www.ietf.org/rfc/rfc2396.txt, section 5.2
    URI newUri;
    //# 3.1
    newUri.scheme    = scheme;
    newUri.schemeStr = schemeStr;
    newUri.query     = other.query;
    newUri.fragment  = other.fragment;
    if (other.authority.size() > 0)
        {
        //# 3.2
        if (absolute || other.absolute)
            newUri.absolute = true;
        newUri.authority = other.authority;
        newUri.port      = other.port;//part of authority
        newUri.path      = other.path;
        }
    else
        {
        //# 3.3
        if (other.absolute)
            {
            newUri.absolute = true;
            newUri.path     = other.path;
            }
        else
            {
            unsigned int pos = path.find_last_of('/');
            if (pos != path.npos)
                {
                String tpath = path.substr(0, pos+1);
                tpath.append(other.path);
                newUri.path = tpath;
                }
            else
                newUri.path = other.path;
            }
        }

    newUri.normalize();
    return newUri;
}


/**
 *  This follows the Java URI algorithm:
 *   1. All "." segments are removed.
 *   2. If a ".." segment is preceded by a non-".." segment
 *          then both of these segments are removed. This step
 *          is repeated until it is no longer applicable.
 *   3. If the path is relative, and if its first segment
 *          contains a colon character (':'), then a "." segment
 *          is prepended. This prevents a relative URI with a path
 *          such as "a:b/c/d" from later being re-parsed as an
 *          opaque URI with a scheme of "a" and a scheme-specific
 *          part of "b/c/d". (Deviation from RFC 2396)
 */
void URI::normalize()
{
    std::vector segments;

    //## Collect segments
    if (path.size()<2)
        return;
    bool abs = false;
    unsigned int pos=0;
    if (path[0]=='/')
        {
        abs = true;
        pos++;
        }
    while (pos < path.size())
        {
        unsigned int pos2 = path.find('/', pos);
        if (pos2==path.npos)
            {
            String seg = path.substr(pos);
            //printf("last segment:%s\n", seg.c_str());
            segments.push_back(seg);
            break;
            }
        if (pos2>pos)
            {
            String seg = path.substr(pos, pos2-pos);
            //printf("segment:%s\n", seg.c_str());
            segments.push_back(seg);
            }
        pos = pos2;
        pos++;
        }

    //## Clean up (normalize) segments
    bool edited = false;
    std::vector::iterator iter;
    for (iter=segments.begin() ; iter!=segments.end() ; )
        {
        String s = *iter;
        if (s == ".")
            {
            iter = segments.erase(iter);
            edited = true;
            }
        else if (s == ".." &&
                 iter != segments.begin() &&
                 *(iter-1) != "..")
            {
            iter--; //back up, then erase two entries
            iter = segments.erase(iter);
            iter = segments.erase(iter);
            edited = true;
            }
        else
            iter++;
        }

    //## Rebuild path, if necessary
    if (edited)
        {
        path.clear();
        if (abs)
            {
            path.append("/");
            }
        std::vector::iterator iter;
        for (iter=segments.begin() ; iter!=segments.end() ; iter++)
            {
            if (iter != segments.begin())
                path.append("/");
            path.append(*iter);
            }
        }

}



//#########################################################################
//# M E S S A G E S
//#########################################################################

void URI::error(const char *fmt, ...)
{
    va_list args;
    fprintf(stderr, "URI error: ");
    va_start(args, fmt);
    vfprintf(stderr, fmt, args);
    va_end(args);
    fprintf(stderr, "\n");
}

void URI::trace(const char *fmt, ...)
{
    va_list args;
    fprintf(stdout, "URI: ");
    va_start(args, fmt);
    vfprintf(stdout, fmt, args);
    va_end(args);
    fprintf(stdout, "\n");
}



//#########################################################################
//# P A R S I N G
//#########################################################################



int URI::peek(int p)
{
    if (p<0 || p>=parselen)
        return -1;
    return parsebuf[p];
}



int URI::match(int p0, char *key)
{
    int p = p0;
    while (p < parselen)
        {
        if (*key == '\0')
            return p;
        else if (*key != parsebuf[p])
            break;
        p++; key++;
        }
    return p0;
}

//#########################################################################
//#  Parsing is performed according to:
//#  http://www.gbiv.com/protocols/uri/rfc/rfc3986.html#components
//#########################################################################

int URI::parseScheme(int p0)
{
    int p = p0;
    for (LookupEntry *entry = schemes; entry->sval ; entry++)
        {
        int p2 = match(p, entry->sval);
        if (p2 > p)
            {
            schemeStr = entry->sval;
            scheme    = entry->ival;
            port      = entry->port;
            p = p2;
            return p;
            }
        }

    return p;
}


int URI::parseHierarchicalPart(int p0)
{
    int p = p0;
    int ch;

    //# Authority field (host and port, for example)
    int p2 = match(p, "//");
    if (p2 > p)
        {
        p = p2;
        portSpecified = false;
        String portStr;
        while (p < parselen)
            {
            ch = peek(p);
            if (ch == '/')
                break;
            else if (ch == ':')
                portSpecified = true;
            else if (portSpecified)
                portStr.push_back((XMLCh)ch);
            else
                authority.push_back((XMLCh)ch);
            p++;
            }
        if (portStr.size() > 0)
            {
            char *pstr = (char *)portStr.c_str();
            char *endStr;
            long val = strtol(pstr, &endStr, 10);
            if (endStr > pstr) //successful parse?
                port = val;
            }
        }

    //# Are we absolute?
    ch = peek(p);
    if (isLetter(ch) && peek(p+1)==':')
        {
        absolute = true;
        path.push_back((XMLCh)'/');
        }
    else if (ch == '/')
        {
        absolute = true;
        if (p>p0) //in other words, if '/' is not the first char
            opaque = true;
        path.push_back((XMLCh)ch);
        p++;
        }

    while (p < parselen)
        {
        ch = peek(p);
        if (ch == '?' || ch == '#')
            break;
        path.push_back((XMLCh)ch);
        p++;
        }

    return p;
}

int URI::parseQuery(int p0)
{
    int p = p0;
    int ch = peek(p);
    if (ch != '?')
        return p0;

    p++;
    while (p < parselen)
        {
        ch = peek(p);
        if (ch == '#')
            break;
        query.push_back((XMLCh)ch);
        p++;
        }


    return p;
}

int URI::parseFragment(int p0)
{

    int p = p0;
    int ch = peek(p);
    if (ch != '#')
        return p0;

    p++;
    while (p < parselen)
        {
        ch = peek(p);
        if (ch == '?')
            break;
        fragment.push_back((XMLCh)ch);
        p++;
        }


    return p;
}


int URI::parse(int p0)
{

    int p = p0;

    int p2 = parseScheme(p);
    if (p2 < 0)
        {
        error("Scheme");
        return -1;
        }
    p = p2;


    p2 = parseHierarchicalPart(p);
    if (p2 < 0)
        {
        error("Hierarchical part");
        return -1;
        }
    p = p2;

    p2 = parseQuery(p);
    if (p2 < 0)
        {
        error("Query");
        return -1;
        }
    p = p2;


    p2 = parseFragment(p);
    if (p2 < 0)
        {
        error("Fragment");
        return -1;
        }
    p = p2;

    return p;

}



bool URI::parse(const String &str)
{

    parselen = str.size();

    String tmp;
    for (unsigned int i=0 ; i tokenize(const String &val,
	                      const String &delimiters);

    /**
     *  replace runs of whitespace with a space
     */
    String strip(const String &s);

    /**
     *  remove leading whitespace from each line
     */
    String leftJustify(const String &s);

    /**
     *  remove leading and trailing whitespace from string
     */
    String trim(const String &s);

    /**
     * Return the native format of the canonical
     * path which we store
     */
    String getNativePath(const String &path);

    /**
     * Execute a shell command.  Outbuf is a ref to a string
     * to catch the result.     
     */	     
    bool executeCommand(const String &call,
	                    const String &inbuf,
						String &outbuf,
						String &errbuf);
    /**
     * List all directories in a given base and starting directory
     * It is usually called like:
     *   	 bool ret = listDirectories("src", "", result);    
     */	     
    bool listDirectories(const String &baseName,
                         const String &dirname,
                         std::vector &res);

    /**
     * Find all files in the named directory whose short names (no path) match
     * the given regex pattern     
     */	     
    bool listFiles(const String &baseName,
                   const String &dirname,
                   std::vector &excludes,
                   std::vector &res);

    /**
     * Parse a 
     */  
    bool getPatternSet(Element *elem,
                       MakeBase &propRef,
					   std::vector &includes,
					   std::vector &excludes);

    /**
     * Parse a  entry, and determine which files
     * should be included
     */  
    bool getFileSet(Element *elem,
                    MakeBase &propRef,
                    String &dir,
					std::vector &result);

    /**
     * Return this object's property list
     */
    virtual std::map &getProperties()
        { return properties; }

    /**
     * Return a named property if found, else a null string
     */
    virtual String getProperty(const String &name)
        {
        String val;
        std::map::iterator iter;
        iter = properties.find(name);
        if (iter != properties.end())
            val = iter->second;
        return val;
        }


    std::map properties;

    /**
     * Turn 'true' and 'false' into boolean values
     */	 	    
    bool getBool(const String &str, bool &val);

    /**
     * Create a directory, making intermediate dirs
     * if necessary
     */	 	 	    
    bool createDirectory(const String &dirname);

    /**
     * Delete a directory and its children if desired
     */
    bool removeDirectory(const String &dirName);

    /**
     * Copy a file from one name to another. Perform only if needed
     */ 
    bool copyFile(const String &srcFile, const String &destFile);

    /**
     * Tests if the file exists and is a regular file
     */ 
    bool isRegularFile(const String &fileName);

    /**
     * Tests if the file exists and is a directory
     */ 
    bool isDirectory(const String &fileName);

    /**
     * Tests is the modification date of fileA is newer than fileB
     */ 
    bool isNewerThan(const String &fileA, const String &fileB);

private:

    /**
     * replace variable refs like ${a} with their values
     */	     
    bool getSubstitutions(const String &s, String &result);



};




/**
 *  Print a printf()-like formatted error message
 */
void MakeBase::error(char *fmt, ...)
{
    va_list args;
    va_start(args,fmt);
    fprintf(stderr, "Make error: ");
    vfprintf(stderr, fmt, args);
    fprintf(stderr, "\n");
    va_end(args) ;
}



/**
 *  Print a printf()-like formatted trace message
 */
void MakeBase::status(char *fmt, ...)
{
    va_list args;
    va_start(args,fmt);
    //fprintf(stdout, " ");
    vfprintf(stdout, fmt, args);
    fprintf(stdout, "\n");
    va_end(args) ;
}



/**
 *  Resolve another path relative to this one
 */
String MakeBase::resolve(const String &otherPath)
{
    URI otherURI(otherPath);
    URI fullURI = uri.resolve(otherURI);
    String ret = fullURI.toString();
    return ret;
}


/**
 *  Print a printf()-like formatted trace message
 */
void MakeBase::trace(char *fmt, ...)
{
    va_list args;
    va_start(args,fmt);
    fprintf(stdout, "Make: ");
    vfprintf(stdout, fmt, args);
    fprintf(stdout, "\n");
    va_end(args) ;
}


/**
 *  Return the suffix, if any, of a file name
 */
String MakeBase::getSuffix(const String &fname)
{
    if (fname.size() < 2)
        return "";
    unsigned int pos = fname.find_last_of('.');
    if (pos == fname.npos)
        return "";
    pos++;
    String res = fname.substr(pos, fname.size()-pos);
    //trace("suffix:%s", res.c_str()); 
    return res;
}



/**
 * Break up a string into substrings delimited the characters
 * in delimiters.  Null-length substrings are ignored
 */  
std::vector MakeBase::tokenize(const String &str,
                                const String &delimiters)
{

    std::vector res;
    char *del = (char *)delimiters.c_str();
    String dmp;
    for (unsigned int i=0 ; i 0)
                {
                res.push_back(dmp);
                dmp.clear();
                }
            }
        else
            {
            dmp.push_back(ch);
            }
        }
    //Add tail
    if (dmp.size() > 0)
        {
        res.push_back(dmp);
        dmp.clear();
        }

    return res;
}



/**
 *  replace runs of whitespace with a single space
 */
String MakeBase::strip(const String &s)
{
    int len = s.size();
    String stripped;
    for (int i = 0 ; i begin ; end--)
        {
        if (!isspace(s[end]))
            break;
        }
    //trace("begin:%d  end:%d", begin, end);

    String res = s.substr(begin, end-begin+1);
    return res;
}

/**
 * Return the native format of the canonical
 * path which we store
 */
String MakeBase::getNativePath(const String &path)
{
#ifdef __WIN32__
    String npath;
    unsigned int firstChar = 0;
    if (path.size() >= 3)
        {
        if (path[0] == '/' &&
            isalpha(path[1]) &&
            path[2] == ':')
            firstChar++;
        }
    for (unsigned int i=firstChar ; i

static String win32LastError()
{

    DWORD dw = GetLastError(); 

    LPVOID str;
    FormatMessage(
        FORMAT_MESSAGE_ALLOCATE_BUFFER | 
        FORMAT_MESSAGE_FROM_SYSTEM,
        NULL,
        dw,
        0,
        (LPTSTR) &str,
        0, NULL );
    LPTSTR p = _tcschr((const char *)str, _T('\r'));
    if(p != NULL)
        { // lose CRLF
        *p = _T('\0');
        }
    String ret = (char *)str;
    LocalFree(str);

    return ret;
}
#endif



/**
 * Execute a system call, using pipes to send data to the
 * program's stdin,  and reading stdout and stderr.
 */
bool MakeBase::executeCommand(const String &command,
                              const String &inbuf,
							  String &outbuf,
							  String &errbuf)
{

    status("============ cmd ============\n%s\n=============================",
	            command.c_str());

#ifdef __WIN32__

    /*
    I really hate having win32 code in this program, but the
    read buffer in command.com and cmd.exe are just too small
    for the large commands we need for compiling and linking.
    */

    bool ret = true;

    //# Allocate a separate buffer for safety
    char *paramBuf = new char[command.size() + 1];
    if (!paramBuf)
       {
       error("executeCommand cannot allocate command buffer");
	   return false;
       }
    strcpy(paramBuf, (char *)command.c_str());

    //# Create pipes
    SECURITY_ATTRIBUTES saAttr; 
    saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); 
    saAttr.bInheritHandle = TRUE; 
    saAttr.lpSecurityDescriptor = NULL; 
    HANDLE stdinRead,  stdinWrite;
    HANDLE stdoutRead, stdoutWrite;
    HANDLE stderrRead, stderrWrite;
    if (!CreatePipe(&stdinRead, &stdinWrite, &saAttr, 0))
	    {
		error("executeProgram: could not create pipe");
        delete[] paramBuf;
		return false;
		} 
    SetHandleInformation(stdinWrite, HANDLE_FLAG_INHERIT, 0);
	if (!CreatePipe(&stdoutRead, &stdoutWrite, &saAttr, 0))
	    {
		error("executeProgram: could not create pipe");
        delete[] paramBuf;
		return false;
		} 
    SetHandleInformation(stdoutRead, HANDLE_FLAG_INHERIT, 0);
	if (!CreatePipe(&stderrRead, &stderrWrite, &saAttr, 0))
	    {
		error("executeProgram: could not create pipe");
        delete[] paramBuf;
		return false;
		} 
    SetHandleInformation(stderrRead, HANDLE_FLAG_INHERIT, 0);

    // Create the process
    STARTUPINFO siStartupInfo;
    PROCESS_INFORMATION piProcessInfo;
    memset(&siStartupInfo, 0, sizeof(siStartupInfo));
    memset(&piProcessInfo, 0, sizeof(piProcessInfo));
    siStartupInfo.cb = sizeof(siStartupInfo);
    siStartupInfo.hStdError   =  stderrWrite;
    siStartupInfo.hStdOutput  =  stdoutWrite;
    siStartupInfo.hStdInput   =  stdinRead;
    siStartupInfo.dwFlags    |=  STARTF_USESTDHANDLES;
   
    if (!CreateProcess(NULL, paramBuf, NULL, NULL, true,
                0, NULL, NULL, &siStartupInfo,
                &piProcessInfo))
        {
        error("executeCommand : could not create process : %s",
		            win32LastError().c_str());
        ret = false;
        }

    DWORD bytesWritten;
    if (inbuf.size()>0 &&
        !WriteFile(stdinWrite, inbuf.c_str(), inbuf.size(), 
               &bytesWritten, NULL))
        {
        error("executeCommand: could not write to pipe");
		return false;
		}	
    if (!CloseHandle(stdinWrite))
	    {	  	
        error("executeCommand: could not close write pipe");
		return false;
		}
    if (!CloseHandle(stdoutWrite))
	    {
        error("executeCommand: could not close read pipe");
		return false;
		}
    if (!CloseHandle(stderrWrite))
	    {
        error("executeCommand: could not close read pipe");
		return false;
		}
	while (true)
        {
        //trace("## stderr");
        DWORD avail;
        if (!PeekNamedPipe(stderrRead, NULL, 0, NULL, &avail, NULL))
            break;
        if (avail > 0)
            {
            DWORD bytesRead = 0;
            char readBuf[1025];
            if (avail>1024) avail = 1024;
            if (!ReadFile(stderrRead, readBuf, avail, &bytesRead, NULL)
                || bytesRead == 0)
                {
                break;
                }
            for (unsigned int i=0 ; i 0)
            {
            DWORD bytesRead = 0;
            char readBuf[1025];
            if (avail>1024) avail = 1024;
            if (!ReadFile(stdoutRead, readBuf, avail, &bytesRead, NULL)
                || bytesRead==0)
                {
                break;
                }
            for (unsigned int i=0 ; i &res)
{
    res.push_back(dirName);
    String fullPath = baseName;
    if (dirName.size()>0)
        {
        fullPath.append("/");
        fullPath.append(dirName);
        }
    DIR *dir = opendir(fullPath.c_str());
    while (true)
        {
        struct dirent *de = readdir(dir);
        if (!de)
            break;

        //Get the directory member name
        String s = de->d_name;
        if (s.size() == 0 || s[0] == '.')
            continue;
        String childName = dirName;
        childName.append("/");
        childName.append(s);

        String fullChildPath = baseName;
        fullChildPath.append("/");
        fullChildPath.append(childName);
        struct stat finfo;
        String childNative = getNativePath(fullChildPath);
        if (stat(childNative.c_str(), &finfo)<0)
            {
            error("cannot stat file:%s", childNative.c_str());
            }
        else if (S_ISDIR(finfo.st_mode))
            {
            //trace("directory: %s", childName.c_str());
            if (!listDirectories(baseName, childName, res))
                return false;
            }
        }
    closedir(dir);

    return true;
}


bool MakeBase::listFiles(const String &baseDir,
                         const String &dirName,
                         std::vector &excludes,
                         std::vector &res)
{
    String fullDir = baseDir;
    if (dirName.size()>0)
        {
        fullDir.append("/");
        fullDir.append(dirName);
        }
    String dirNative = getNativePath(fullDir);

    std::vector subdirs;
    DIR *dir = opendir(dirNative.c_str());
    while (true)
        {
        struct dirent *de = readdir(dir);
        if (!de)
            break;

        //Get the directory member name
        String s = de->d_name;
        if (s.size() == 0 || s[0] == '.')
            continue;
        String childName;
        if (dirName.size()>0)
            {
            childName.append(dirName);
            childName.append("/");
            }
        childName.append(s);
        String fullChild = baseDir;
        fullChild.append("/");
        fullChild.append(childName);
        
        if (std::find(excludes.begin(), excludes.end(), childName)
		        != excludes.end())
            {
            //trace("EXCLUDED:%s", childName.c_str());
            continue;
            }

        struct stat finfo;
        String nativeName = getNativePath(fullChild);
        if (stat(nativeName.c_str(), &finfo)<0)
            {
            error("cannot stat file:%s", childName.c_str());
            return false;
            }
        else if (S_ISDIR(finfo.st_mode))
            {
            //trace("directory: %s", childName.c_str());
            if (!listFiles(baseDir, childName, excludes, res))
                return false;
            }
        else if (!S_ISREG(finfo.st_mode))
            {
            trace("not regular: %s", childName.c_str());
            }
        else
            {
            res.push_back(childName);
            }
        }
    closedir(dir);

    return true;
}








bool MakeBase::getSubstitutions(const String &str, String &result)
{
    String s = trim(str);
    int len = (int)s.size();
    String val;
    for (int i=0 ; i::iterator iter;
		            iter = properties.find(trim(varname));
		            if (iter != properties.end())
		                {
		                val.append(iter->second);
		                }
		            else
		                {
		                error("property ${%s} not found", varname.c_str());
		                return false;
		                }
		            break;
		            }
		        else
		            {
		            varname.push_back(ch);
		            }
		        }
		    i = j;
			}
		else
		    {
		    val.push_back(ch);
		    }
        }
    result = val;
    return true;
}


bool MakeBase::getAttribute(Element *elem, const String &name,
                                    String &result)
{
    String s = elem->getAttribute(name);
    return getSubstitutions(s, result);
}


bool MakeBase::getValue(Element *elem, String &result)
{
    String s = elem->getValue();
    //Replace all runs of whitespace with a single space
    return getSubstitutions(s, result);
}


/**
 * Turn 'true' and 'false' into boolean values
 */	 	    
bool MakeBase::getBool(const String &str, bool &val)
{
    if (str == "true")
        val = true;
    else if (str == "false")
        val = false;
    else
        {
        error("expected 'true' or 'false'.  found '%s'", str.c_str());
        return false;
        }
    return true;
}




/**
 * Parse a  entry
 */  
bool MakeBase::getPatternSet(Element *elem,
                          MakeBase &propRef,
						  std::vector &includes,
						  std::vector &excludes
						  )
{
    std::vector children  = elem->getChildren();
    for (unsigned int i=0 ; igetName();
        if (tagName == "exclude")
            {
            String fname;
			if (!propRef.getAttribute(child, "name", fname))
			    return false;
            //trace("EXCLUDE: %s", fname.c_str());
            excludes.push_back(fname);
            }
        else if (tagName == "include")
            {
            String fname;
			if (!propRef.getAttribute(child, "name", fname))
			    return false;
            //trace("INCLUDE: %s", fname.c_str());
            includes.push_back(fname);
            }
        }

    return true;
}




/**
 * Parse a  entry, and determine which files
 * should be included
 */  
bool MakeBase::getFileSet(Element *elem,
                          MakeBase &propRef,
						  String &dir,
						  std::vector &result)
{
    String name = elem->getName();
    if (name != "fileset")
        {
        error("expected ");
        return false;
        }


    std::vector includes;
    std::vector excludes;

    //A fileset has one implied patternset
    if (!getPatternSet(elem, propRef, includes, excludes))
        {
        return false;
        }
    //Look for child tags, including more patternsets
    std::vector children  = elem->getChildren();
    for (unsigned int i=0 ; igetName();
        if (tagName == "patternset")
            {
            if (!getPatternSet(child, propRef, includes, excludes))
                {
                return false;
                }
            }
        }

    //Now do the stuff
    //Get the base directory for reading file names
    if (!propRef.getAttribute(elem, "dir", dir))
        return false;

    std::vector fileList;
    if (dir.size() > 0)
        {
        String baseDir = propRef.resolve(dir);
	    if (!listFiles(baseDir, "", excludes, fileList))
	        return false;
	    }
	
	std::vector::iterator iter;
    for (iter=includes.begin() ; iter!=includes.end() ; iter++)
        {
        String fname = *iter;
        fileList.push_back(fname);
        }
        
	result = fileList;
	
	/*
	for (unsigned int i=0 ; id_name;
        if (s.size() == 0 || s[0] == '.')
            continue;
        String childName;
        if (dirName.size() > 0)
            {
            childName.append(dirName);
            childName.append("/");
            }
        childName.append(s);


        struct stat finfo;
        String childNative = getNativePath(childName);
        char *cnative = (char *)childNative.c_str();
        if (stat(cnative, &finfo)<0)
            {
            error("cannot stat file:%s", cnative);
            }
        else if (S_ISDIR(finfo.st_mode))
            {
            //trace("DEL dir: %s", childName.c_str());
			if (!removeDirectory(childName))
    		    {
			    return false;
			    }
            }
        else if (!S_ISREG(finfo.st_mode))
            {
            trace("not regular: %s", cnative);
            }
        else
            {
            //trace("DEL file: %s", childName.c_str());
            if (remove(cnative)<0)
                {
                error("error deleting %s : %s",
				     cnative, strerror(errno));
				return false;
				}
            }
        }
    closedir(dir);

    //Now delete the directory
    String native = getNativePath(dirName);
    if (rmdir(native.c_str())<0)
        {
        error("could not delete directory %s : %s",
            native.c_str() , strerror(errno));
        return false;
        }

    return true;
    
}


/**
 * Copy a file from one name to another. Perform only if needed
 */ 
bool MakeBase::copyFile(const String &srcFile, const String &destFile)
{
    //# 1 Check up-to-date times
    String srcNative = getNativePath(srcFile);
    struct stat srcinfo;
    if (stat(srcNative.c_str(), &srcinfo)<0)
        {
        error("source file %s for copy does not exist",
		         srcNative.c_str());
        return false;
        }

    String destNative = getNativePath(destFile);
    struct stat destinfo;
    if (stat(destNative.c_str(), &destinfo)==0)
        {
        if (destinfo.st_mtime >= srcinfo.st_mtime)
            return true;
        }
        
    //# 2 prepare a destination directory if necessary
    unsigned int pos = destFile.find_last_of('/');
    if (pos != destFile.npos)
        {
        String subpath = destFile.substr(0, pos);
        if (!createDirectory(subpath))
            return false;
        }

    //# 3 do the data copy
#ifndef __WIN32__

    FILE *srcf = fopen(srcNative.c_str(), "rb");
    if (!srcf)
        {
        error("copyFile cannot open '%s' for reading", srcNative.c_str());
        return false;
        }
    FILE *destf = fopen(destNative.c_str(), "wb");
    if (!destf)
        {
        error("copyFile cannot open %s for writing", srcNative.c_str());
        return false;
        }

    while (!feof(srcf))
        {
        int ch = fgetc(srcf);
        if (ch<0)
            break;
        fputc(ch, destf);
        }

    fclose(destf);
    fclose(srcf);

#else
    
    if (!CopyFile(srcNative.c_str(), destNative.c_str(), false))
        {
        error("copyFile from %s to %s failed",
		     srcNative.c_str(), destNative.c_str());
        return false;
        }
        
#endif /* __WIN32__ */


    return true;
}



/**
 * Tests if the file exists and is a regular file
 */ 
bool MakeBase::isRegularFile(const String &fileName)
{
    String native = getNativePath(fileName);
    struct stat finfo;
    
    //Exists?
    if (stat(native.c_str(), &finfo)<0)
		return false;


    //check the file mode
    if (!S_ISREG(finfo.st_mode))
		return false;

    return true;
}

/**
 * Tests if the file exists and is a directory
 */ 
bool MakeBase::isDirectory(const String &fileName)
{
    String native = getNativePath(fileName);
    struct stat finfo;
    
    //Exists?
    if (stat(native.c_str(), &finfo)<0)
		return false;


    //check the file mode
    if (!S_ISDIR(finfo.st_mode))
		return false;

    return true;
}



/**
 * Tests is the modification of fileA is newer than fileB
 */ 
bool MakeBase::isNewerThan(const String &fileA, const String &fileB)
{
    //trace("isNewerThan:'%s' , '%s'", fileA.c_str(), fileB.c_str());
    String nativeA = getNativePath(fileA);
    struct stat infoA;
    //IF source does not exist, NOT newer
    if (stat(nativeA.c_str(), &infoA)<0)
        {
		return false;
		}

    String nativeB = getNativePath(fileB);
    struct stat infoB;
    //IF dest does not exist, YES, newer
    if (stat(nativeB.c_str(), &infoB)<0)
        {
		return true;
		}

    //check the actual times
    if (infoA.st_mtime > infoB.st_mtime)
        {
		return true;
		}

    return false;
}


//########################################################################
//# P K G    C O N F I G
//########################################################################

/**
 *
 */
class PkgConfig : public MakeBase
{

public:

    /**
     *
     */
    PkgConfig()
        { init(); }

    /**
     *
     */
    PkgConfig(const String &namearg)
        { init(); name = namearg; }

    /**
     *
     */
    PkgConfig(const PkgConfig &other)
        { assign(other); }

    /**
     *
     */
    PkgConfig &operator=(const PkgConfig &other)
        { assign(other); return *this; }

    /**
     *
     */
    virtual ~PkgConfig()
        { }

    /**
     *
     */
    virtual String getName()
        { return name; }

    /**
     *
     */
    virtual String getDescription()
        { return description; }

    /**
     *
     */
    virtual String getCflags()
        { return cflags; }

    /**
     *
     */
    virtual String getLibs()
        { return libs; }

    /**
     *
     */
    virtual String getVersion()
        { return version; }

    /**
     *
     */
    virtual int getMajorVersion()
        { return majorVersion; }

    /**
     *
     */
    virtual int getMinorVersion()
        { return minorVersion; }

    /**
     *
     */
    virtual int getMicroVersion()
        { return microVersion; }

    /**
     *
     */
    virtual std::map &getAttributes()
        { return attrs; }

    /**
     *
     */
    virtual std::vector &getRequireList()
        { return requireList; }

    virtual bool readFile(const String &fileName);

private:

    void init()
        {
        name         = "";
        description  = "";
        cflags       = "";
        libs         = "";
        requires     = "";
        version      = "";
        majorVersion = 0;
        minorVersion = 0;
        microVersion = 0;
        fileName     = "";
        attrs.clear();
        requireList.clear();
        }

    void assign(const PkgConfig &other)
        {
        name         = other.name;
        description  = other.description;
        cflags       = other.cflags;
        libs         = other.libs;
        requires     = other.requires;
        version      = other.version;
        majorVersion = other.majorVersion;
        minorVersion = other.minorVersion;
        microVersion = other.microVersion;
        fileName     = other.fileName;
        attrs        = other.attrs;
        requireList  = other.requireList;
        }



    int get(int pos);

    int skipwhite(int pos);

    int getword(int pos, String &ret);

    void parseRequires();

    void parseVersion();

    bool parse(const String &buf);

    void dumpAttrs();

    String name;

    String description;

    String cflags;

    String libs;

    String requires;

    String version;

    int majorVersion;

    int minorVersion;

    int microVersion;

    String fileName;

    std::map attrs;

    std::vector requireList;

    char *parsebuf;
    int parselen;
};


/**
 * Get a character from the buffer at pos.  If out of range,
 * return -1 for safety
 */
int PkgConfig::get(int pos)
{
    if (pos>parselen)
        return -1;
    return parsebuf[pos];
}



/**
 *  Skip over all whitespace characters beginning at pos.  Return
 *  the position of the first non-whitespace character.
 */
int PkgConfig::skipwhite(int pos)
{
    while (pos < parselen)
        {
        int ch = get(pos);
        if (ch < 0)
            break;
        if (!isspace(ch))
            break;
        pos++;
        }
    return pos;
}


/**
 *  Parse the buffer beginning at pos, for a word.  Fill
 *  'ret' with the result.  Return the position after the
 *  word.
 */
int PkgConfig::getword(int pos, String &ret)
{
    while (pos < parselen)
        {
        int ch = get(pos);
        if (ch < 0)
            break;
        if (!isalnum(ch) && ch != '_' && ch != '-'&& ch != '.')
            break;
        ret.push_back((char)ch);
        pos++;
        }
    return pos;
}

void PkgConfig::parseRequires()
{
    if (requires.size() == 0)
        return;
    parsebuf = (char *)requires.c_str();
    parselen = requires.size();
    int pos = 0;
    while (pos < parselen)
        {
        pos = skipwhite(pos);
        String val;
        int pos2 = getword(pos, val);
        if (pos2 == pos)
            break;
        pos = pos2;
        //trace("val %s", val.c_str());
        requireList.push_back(val);
        }
}

static int getint(const String str)
{
    char *s = (char *)str.c_str();
    char *ends = NULL;
    long val = strtol(s, &ends, 10);
    if (ends == s)
        return 0L;
    else
        return val;
}

void PkgConfig::parseVersion()
{
    if (version.size() == 0)
        return;
    String s1, s2, s3;
    unsigned int pos = 0;
    unsigned int pos2 = version.find('.', pos);
    if (pos2 == version.npos)
        {
        s1 = version;
        }
    else
        {
        s1 = version.substr(pos, pos2-pos);
        pos = pos2;
        pos++;
        if (pos < version.size())
            {
            pos2 = version.find('.', pos);
            if (pos2 == version.npos)
                {
                s2 = version.substr(pos, version.size()-pos);
                }
            else
                {
                s2 = version.substr(pos, pos2-pos);
                pos = pos2;
                pos++;
                if (pos < version.size())
                    s3 = version.substr(pos, pos2-pos);
                }
            }
        }

    majorVersion = getint(s1);
    minorVersion = getint(s2);
    microVersion = getint(s3);
    //trace("version:%d.%d.%d", majorVersion,
    //          minorVersion, microVersion );
}


bool PkgConfig::parse(const String &buf)
{
    init();

    parsebuf = (char *)buf.c_str();
    parselen = buf.size();
    int pos = 0;


    while (pos < parselen)
        {
        String attrName;
        pos = skipwhite(pos);
        int ch = get(pos);
        if (ch == '#')
            {
            //comment.  eat the rest of the line
            while (pos < parselen)
                {
                ch = get(pos);
                if (ch == '\n' || ch < 0)
                    break;
                pos++;
                }
            continue;
            }
        pos = getword(pos, attrName);
        if (attrName.size() == 0)
            continue;
        pos = skipwhite(pos);
        ch = get(pos);
        if (ch != ':' && ch != '=')
            {
            error("expected ':' or '='");
            return false;
            }
        pos++;
        pos = skipwhite(pos);
        String attrVal;
        while (pos < parselen)
            {
            ch = get(pos);
            if (ch == '\n' || ch < 0)
                break;
            else if (ch == '$' && get(pos+1) == '{')
                {
                //#  this is a ${substitution}
                pos += 2;
                String subName;
                while (pos < parselen)
                    {
                    ch = get(pos);
                    if (ch < 0)
                        {
                        error("unterminated substitution");
                        return false;
                        }
                    else if (ch == '}')
                        break;
                    else
                        subName.push_back((char)ch);
                    pos++;
                    }
                //trace("subName:%s", subName.c_str());
                String subVal = attrs[subName];
                //trace("subVal:%s", subVal.c_str());
                attrVal.append(subVal);
                }
            else
                attrVal.push_back((char)ch);
            pos++;
            }

        attrVal = trim(attrVal);
        attrs[attrName] = attrVal;

        if (attrName == "Name")
            name = attrVal;
        else if (attrName == "Description")
            description = attrVal;
        else if (attrName == "Cflags")
            cflags = attrVal;
        else if (attrName == "Libs")
            libs = attrVal;
        else if (attrName == "Requires")
            requires = attrVal;
        else if (attrName == "Version")
            version = attrVal;

        //trace("name:'%s'  value:'%s'",
        //      attrName.c_str(), attrVal.c_str());
        }


    parseRequires();
    parseVersion();

    return true;
}

void PkgConfig::dumpAttrs()
{
    trace("### PkgConfig attributes for %s", fileName.c_str());
    std::map::iterator iter;
    for (iter=attrs.begin() ; iter!=attrs.end() ; iter++)
        {
        trace("   %s = %s", iter->first.c_str(), iter->second.c_str());
        }
}


bool PkgConfig::readFile(const String &fileNameArg)
{
    fileName = fileNameArg;

    FILE *f = fopen(fileName.c_str(), "r");
    if (!f)
        {
        error("cannot open file '%s' for reading", fileName.c_str());
        return false;
        }
    String buf;
    while (true)
        {
        int ch = fgetc(f);
        if (ch < 0)
            break;
        buf.push_back((char)ch);
        }
    fclose(f);

    trace("####### File:\n%s", buf.c_str());
    if (!parse(buf))
        {
        return false;
        }

    dumpAttrs();

    return true;
}





//########################################################################
//# D E P T O O L
//########################################################################



/**
 *  Class which holds information for each file.
 */
class FileRec
{
public:

    typedef enum
        {
        UNKNOWN,
        CFILE,
        HFILE,
        OFILE
        } FileType;

    /**
     *  Constructor
     */
    FileRec()
        {init(); type = UNKNOWN;}

    /**
     *  Copy constructor
     */
    FileRec(const FileRec &other)
        {init(); assign(other);}
    /**
     *  Constructor
     */
    FileRec(int typeVal)
        {init(); type = typeVal;}
    /**
     *  Assignment operator
     */
    FileRec &operator=(const FileRec &other)
        {init(); assign(other); return *this;}


    /**
     *  Destructor
     */
    ~FileRec()
        {}

    /**
     *  Directory part of the file name
     */
    String path;

    /**
     *  Base name, sans directory and suffix
     */
    String baseName;

    /**
     *  File extension, such as cpp or h
     */
    String suffix;

    /**
     *  Type of file: CFILE, HFILE, OFILE
     */
    int type;

    /**
     * Used to list files ref'd by this one
     */
    std::map files;


private:

    void init()
        {
        }

    void assign(const FileRec &other)
        {
        type     = other.type;
        baseName = other.baseName;
        suffix   = other.suffix;
        files    = other.files;
        }

};



/**
 *  Simpler dependency record
 */
class DepRec
{
public:

    /**
     *  Constructor
     */
    DepRec()
        {init();}

    /**
     *  Copy constructor
     */
    DepRec(const DepRec &other)
        {init(); assign(other);}
    /**
     *  Constructor
     */
    DepRec(const String &fname)
        {init(); name = fname; }
    /**
     *  Assignment operator
     */
    DepRec &operator=(const DepRec &other)
        {init(); assign(other); return *this;}


    /**
     *  Destructor
     */
    ~DepRec()
        {}

    /**
     *  Directory part of the file name
     */
    String path;

    /**
     *  Base name, without the path and suffix
     */
    String name;

    /**
     *  Suffix of the source
     */
    String suffix;


    /**
     * Used to list files ref'd by this one
     */
    std::vector files;


private:

    void init()
        {
        }

    void assign(const DepRec &other)
        {
        path     = other.path;
        name     = other.name;
        suffix   = other.suffix;
        files    = other.files;
        }

};


class DepTool : public MakeBase
{
public:

    /**
     *  Constructor
     */
    DepTool()
        {init();}

    /**
     *  Copy constructor
     */
    DepTool(const DepTool &other)
        {init(); assign(other);}

    /**
     *  Assignment operator
     */
    DepTool &operator=(const DepTool &other)
        {init(); assign(other); return *this;}


    /**
     *  Destructor
     */
    ~DepTool()
        {}


    /**
     *  Reset this section of code
     */
    virtual void init();
    
    /**
     *  Reset this section of code
     */
    virtual void assign(const DepTool &other)
        {
        }
    
    /**
     *  Sets the source directory which will be scanned
     */
    virtual void setSourceDirectory(const String &val)
        { sourceDir = val; }

    /**
     *  Returns the source directory which will be scanned
     */
    virtual String getSourceDirectory()
        { return sourceDir; }

    /**
     *  Sets the list of files within the directory to analyze
     */
    virtual void setFileList(const std::vector &list)
        { fileList = list; }

    /**
     * Creates the list of all file names which will be
     * candidates for further processing.  Reads make.exclude
     * to see which files for directories to leave out.
     */
    virtual bool createFileList();


    /**
     *  Generates the forward dependency list
     */
    virtual bool generateDependencies();


    /**
     *  Generates the forward dependency list, saving the file
     */
    virtual bool generateDependencies(const String &);


    /**
     *  Load a dependency file
     */
    std::vector loadDepFile(const String &fileName);

    /**
     *  Load a dependency file, generating one if necessary
     */
    std::vector getDepFile(const String &fileName);

    /**
     *  Save a dependency file
     */
    bool saveDepFile(const String &fileName);


private:


    /**
     *
     */
    void parseName(const String &fullname,
                   String &path,
                   String &basename,
                   String &suffix);

    /**
     *
     */
    int get(int pos);

    /**
     *
     */
    int skipwhite(int pos);

    /**
     *
     */
    int getword(int pos, String &ret);

    /**
     *
     */
    bool sequ(int pos, char *key);

    /**
     *
     */
    bool addIncludeFile(FileRec *frec, const String &fname);

    /**
     *
     */
    bool scanFile(const String &fname, FileRec *frec);

    /**
     *
     */
    bool processDependency(FileRec *ofile,
                           FileRec *include,
                           int depth);

    /**
     *
     */
    String sourceDir;

    /**
     *
     */
    std::vector fileList;

    /**
     *
     */
    std::vector directories;

    /**
     * A list of all files which will be processed for
     * dependencies.  This is the only list that has the actual
     * records.  All other lists have pointers to these records.     
     */
    std::map allFiles;

    /**
     * The list of .o files, and the
     * dependencies upon them.
     */
    std::map depFiles;

    int depFileSize;
    char *depFileBuf;
    

};





/**
 *  Clean up after processing.  Called by the destructor, but should
 *  also be called before the object is reused.
 */
void DepTool::init()
{
    sourceDir = ".";

    fileList.clear();
    directories.clear();
    
    //clear refs
    depFiles.clear();
    //clear records
    std::map::iterator iter;
    for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
         delete iter->second;

    allFiles.clear(); 

}




/**
 *  Parse a full path name into path, base name, and suffix
 */
void DepTool::parseName(const String &fullname,
                        String &path,
                        String &basename,
                        String &suffix)
{
    if (fullname.size() < 2)
        return;

    unsigned int pos = fullname.find_last_of('/');
    if (pos != fullname.npos && pospath            = path;
            fe->baseName        = basename;
            fe->suffix          = sfx;
            allFiles[fileName]  = fe;
            }
        else if (sfx == "h"   ||  sfx == "hh"  ||
                 sfx == "hpp" ||  sfx == "hxx")
            {
            FileRec *fe         = new FileRec(FileRec::HFILE);
            fe->path            = path;
            fe->baseName        = basename;
            fe->suffix          = sfx;
            allFiles[fileName]  = fe;
            }
        }

    if (!listDirectories(sourceDir, "", directories))
        return false;
        
    return true;
}





/**
 * Get a character from the buffer at pos.  If out of range,
 * return -1 for safety
 */
int DepTool::get(int pos)
{
    if (pos>depFileSize)
        return -1;
    return depFileBuf[pos];
}



/**
 *  Skip over all whitespace characters beginning at pos.  Return
 *  the position of the first non-whitespace character.
 */
int DepTool::skipwhite(int pos)
{
    while (pos < depFileSize)
        {
        int ch = get(pos);
        if (ch < 0)
            break;
        if (!isspace(ch))
            break;
        pos++;
        }
    return pos;
}


/**
 *  Parse the buffer beginning at pos, for a word.  Fill
 *  'ret' with the result.  Return the position after the
 *  word.
 */
int DepTool::getword(int pos, String &ret)
{
    while (pos < depFileSize)
        {
        int ch = get(pos);
        if (ch < 0)
            break;
        if (isspace(ch))
            break;
        ret.push_back((char)ch);
        pos++;
        }
    return pos;
}

/**
 * Return whether the sequence of characters in the buffer
 * beginning at pos match the key,  for the length of the key
 */
bool DepTool::sequ(int pos, char *key)
{
    while (*key)
        {
        if (*key != get(pos))
            return false;
        key++; pos++;
        }
    return true;
}



/**
 *  Add an include file name to a file record.  If the name
 *  is not found in allFiles explicitly, try prepending include
 *  directory names to it and try again.
 */
bool DepTool::addIncludeFile(FileRec *frec, const String &iname)
{

    std::map::iterator iter =
           allFiles.find(iname);
    if (iter != allFiles.end()) //already exists
        {
         //h file in same dir
        FileRec *other = iter->second;
        //trace("local: '%s'", iname.c_str());
        frec->files[iname] = other;
        return true;
        }
    else 
        {
        //look in other dirs
        std::vector::iterator diter;
        for (diter=directories.begin() ;
             diter!=directories.end() ; diter++)
            {
            String dfname = *diter;
            dfname.append("/");
            dfname.append(iname);
            iter = allFiles.find(dfname);
            if (iter != allFiles.end())
                {
                FileRec *other = iter->second;
                //trace("other: '%s'", iname.c_str());
                frec->files[dfname] = other;
                return true;
                }
            }
        }
    return true;
}



/**
 *  Lightly parse a file to find the #include directives.  Do
 *  a bit of state machine stuff to make sure that the directive
 *  is valid.  (Like not in a comment).
 */
bool DepTool::scanFile(const String &fname, FileRec *frec)
{
    String fileName;
    if (sourceDir.size() > 0)
        {
        fileName.append(sourceDir);
        fileName.append("/");
        }
    fileName.append(fname);
    String nativeName = getNativePath(fileName);
    FILE *f = fopen(nativeName.c_str(), "r");
    if (!f)
        {
        error("Could not open '%s' for reading", fname.c_str());
        return false;
        }
    String buf;
    while (true)
        {
        int ch = fgetc(f);
        if (ch < 0)
            break;
        buf.push_back((char)ch);
        }
    fclose(f);

    depFileSize = buf.size();
    depFileBuf  = (char *)buf.c_str();
    int pos = 0;


    while (pos < depFileSize)
        {
        //trace("p:%c", get(pos));

        //# Block comment
        if (get(pos) == '/' && get(pos+1) == '*')
            {
            pos += 2;
            while (pos < depFileSize)
                {
                if (get(pos) == '*' && get(pos+1) == '/')
                    {
                    pos += 2;
                    break;
                    }
                else
                    pos++;
                }
            }
        //# Line comment
        else if (get(pos) == '/' && get(pos+1) == '/')
            {
            pos += 2;
            while (pos < depFileSize)
                {
                if (get(pos) == '\n')
                    {
                    pos++;
                    break;
                    }
                else
                    pos++;
                }
            }
        //# #include! yaay
        else if (sequ(pos, "#include"))
            {
            pos += 8;
            pos = skipwhite(pos);
            String iname;
            pos = getword(pos, iname);
            if (iname.size()>2)
                {
                iname = iname.substr(1, iname.size()-2);
                addIncludeFile(frec, iname);
                }
            }
        else
            {
            pos++;
            }
        }

    return true;
}



/**
 *  Recursively check include lists to find all files in allFiles to which
 *  a given file is dependent.
 */
bool DepTool::processDependency(FileRec *ofile,
                             FileRec *include,
                             int depth)
{
    std::map::iterator iter;
    for (iter=include->files.begin() ; iter!=include->files.end() ; iter++)
        {
        String fname  = iter->first;
        if (ofile->files.find(fname) != ofile->files.end())
            {
            //trace("file '%s' already seen", fname.c_str());
            continue;
            }
        FileRec *child  = iter->second;
        ofile->files[fname] = child;
      
        processDependency(ofile, child, depth+1);
        }


    return true;
}





/**
 *  Generate the file dependency list.
 */
bool DepTool::generateDependencies()
{
    std::map::iterator iter;
    //# First pass.  Scan for all includes
    for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
        {
        FileRec *frec = iter->second;
        if (!scanFile(iter->first, frec))
            {
            //quit?
            }
        }

    //# Second pass.  Scan for all includes
    for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
        {
        FileRec *include = iter->second;
        if (include->type == FileRec::CFILE)
            {
            String cFileName = iter->first;
            FileRec *ofile      = new FileRec(FileRec::OFILE);
            ofile->path         = include->path;
            ofile->baseName     = include->baseName;
            ofile->suffix       = include->suffix;
            String fname     = include->path;
            if (fname.size()>0)
                fname.append("/");
            fname.append(include->baseName);
            fname.append(".o");
            depFiles[fname]    = ofile;
            //add the .c file first?   no, don't
            //ofile->files[cFileName] = include;
            
            //trace("ofile:%s", fname.c_str());

            processDependency(ofile, include, 0);
            }
        }

      
    return true;
}



/**
 *  High-level call to generate deps and optionally save them
 */
bool DepTool::generateDependencies(const String &fileName)
{
    if (!createFileList())
        return false;
    if (!generateDependencies())
        return false;
    if (!saveDepFile(fileName))
        return false;
    return true;
}


/**
 *   This saves the dependency cache.
 */
bool DepTool::saveDepFile(const String &fileName)
{
    time_t tim;
    time(&tim);

    FILE *f = fopen(fileName.c_str(), "w");
    if (!f)
        {
        trace("cannot open '%s' for writing", fileName.c_str());
        }
    fprintf(f, "\n");
    fprintf(f, "\n");

    fprintf(f, "\n\n", sourceDir.c_str());
    std::map::iterator iter;
    for (iter=depFiles.begin() ; iter!=depFiles.end() ; iter++)
        {
        FileRec *frec = iter->second;
        if (frec->type == FileRec::OFILE)
            {
            fprintf(f, "\n",
			     frec->path.c_str(), frec->baseName.c_str(), frec->suffix.c_str());
            std::map::iterator citer;
            for (citer=frec->files.begin() ; citer!=frec->files.end() ; citer++)
                {
                String cfname = citer->first;
                fprintf(f, "    \n", cfname.c_str());
                }
            fprintf(f, "\n\n");
            }
        }

    fprintf(f, "\n");
    fprintf(f, "\n");
    fprintf(f, "\n");

    fclose(f);

    return true;
}




/**
 *   This loads the dependency cache.
 */
std::vector DepTool::loadDepFile(const String &depFile)
{
    std::vector result;
    
    Parser parser;
    Element *root = parser.parseFile(depFile.c_str());
    if (!root)
        {
        error("Could not open %s for reading", depFile.c_str());
        return result;
        }

    if (root->getChildren().size()==0 ||
        root->getChildren()[0]->getName()!="dependencies")
        {
        error("Main xml element should be ");
        delete root;
        return result;
        }

    //########## Start parsing
    Element *depList = root->getChildren()[0];

    std::vector objects = depList->getChildren();
    for (unsigned int i=0 ; igetName();
        if (tagName == "object")
            {
            String objName   = objectElem->getAttribute("name");
             //trace("object:%s", objName.c_str());
            DepRec depObject(objName);
            depObject.path   = objectElem->getAttribute("path");
            depObject.suffix = objectElem->getAttribute("suffix");
            //########## DESCRIPTION
            std::vector depElems = objectElem->getChildren();
            for (unsigned int i=0 ; igetName();
                if (tagName == "dep")
                    {
                    String depName = depElem->getAttribute("name");
                    //trace("    dep:%s", depName.c_str());
                    depObject.files.push_back(depName);
                    }
                }
            //Insert into the result list, in a sorted manner
            bool inserted = false;
            std::vector::iterator iter;
            for (iter = result.begin() ; iter != result.end() ; iter++)
                {
                String vpath = iter->path;
                vpath.append("/");
                vpath.append(iter->name);
                String opath = depObject.path;
                opath.append("/");
                opath.append(depObject.name);
                if (vpath > opath)
                    {
                    inserted = true;
                    iter = result.insert(iter, depObject);
                    break;
                    }
                }
            if (!inserted)
                result.push_back(depObject);
            }
        }

    delete root;

    return result;
}


/**
 *   This loads the dependency cache.
 */
std::vector DepTool::getDepFile(const String &depFile)
{
    std::vector result = loadDepFile(depFile);
    if (result.size() == 0)
        {
        generateDependencies(depFile);
        result = loadDepFile(depFile);
        }
    return result;
}




//########################################################################
//# T A S K
//########################################################################
//forward decl
class Target;
class Make;

/**
 *
 */
class Task : public MakeBase
{

public:

    typedef enum
        {
        TASK_NONE,
        TASK_AR,
        TASK_CC,
        TASK_COPY,
        TASK_DELETE,
        TASK_JAR,
        TASK_JAVAC,
        TASK_LINK,
        TASK_MAKEFILE,
        TASK_MKDIR,
        TASK_MSGFMT,
        TASK_RANLIB,
        TASK_RC,
        TASK_STRIP,
        TASK_TSTAMP
        } TaskType;
        

    /**
     *
     */
    Task(MakeBase &par) : parent(par)
        { init(); }

    /**
     *
     */
    Task(const Task &other) : parent(other.parent)
        { init(); assign(other); }

    /**
     *
     */
    Task &operator=(const Task &other)
        { assign(other); return *this; }

    /**
     *
     */
    virtual ~Task()
        { }


    /**
     *
     */
    virtual MakeBase &getParent()
        { return parent; }

     /**
     *
     */
    virtual int  getType()
        { return type; }

    /**
     *
     */
    virtual void setType(int val)
        { type = val; }

    /**
     *
     */
    virtual String getName()
        { return name; }

    /**
     *
     */
    virtual bool execute()
        { return true; }

    /**
     *
     */
    virtual bool parse(Element *elem)
        { return true; }

    /**
     *
     */
    Task *createTask(Element *elem);


protected:

    void init()
        {
        type = TASK_NONE;
        name = "none";
        }

    void assign(const Task &other)
        {
        type = other.type;
        name = other.name;
        }
        
    String getAttribute(Element *elem, const String &attrName)
        {
        String str;
        return str;
        }

    MakeBase &parent;

    int type;

    String name;
};




/**
 * Run the "ar" command to archive .o's into a .a
 */
class TaskAr : public Task
{
public:

    TaskAr(MakeBase &par) : Task(par)
        {
		type = TASK_AR; name = "ar";
		command = "ar crv";
		}

    virtual ~TaskAr()
        {}

    virtual bool execute()
        {
        //trace("###########HERE %d", fileSet.size());
        bool doit = false;
        
        String fullOut = parent.resolve(fileName);
        //trace("ar fullout: %s", fullOut.c_str());
        

        for (unsigned int i=0 ; i0)
			    {
			    fname.append(fileSetDir);
                fname.append("/");
                }
            fname.append(fileSet[i]);
            String fullName = parent.resolve(fname);
            //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
            if (isNewerThan(fullName, fullOut))
                doit = true;
            }
        //trace("Needs it:%d", doit);
        if (!doit)
            {
            return true;
            }

        String cmd = command;
        cmd.append(" ");
        cmd.append(fullOut);
        for (unsigned int i=0 ; i0)
			    {
			    fname.append(fileSetDir);
                fname.append("/");
                }
            fname.append(fileSet[i]);
            String fullName = parent.resolve(fname);

            cmd.append(" ");
            cmd.append(fullName);
            }

        String outString, errString;
        if (!executeCommand(cmd.c_str(), "", outString, errString))
            {
            error("AR problem: %s", errString.c_str());
            return false;
            }

        return true;
        }

    virtual bool parse(Element *elem)
        {
        if (!parent.getAttribute(elem, "file", fileName))
            return false;
            
        std::vector children = elem->getChildren();
        for (unsigned int i=0 ; igetName();
            if (tagName == "fileset")
                {
                if (!getFileSet(child, parent, fileSetDir, fileSet))
                    return false;
                }
            }
        return true;
        }

private:

    String command;
    String fileName;
    String fileSetDir;
    std::vector fileSet;

};


/**
 * This task runs the C/C++ compiler.  The compiler is invoked
 * for all .c or .cpp files which are newer than their correcsponding
 * .o files.  
 */
class TaskCC : public Task
{
public:

    TaskCC(MakeBase &par) : Task(par)
        {
		type = TASK_CC; name = "cc";
		ccCommand   = "gcc";
		cxxCommand  = "g++";
		source      = ".";
		dest        = ".";
		flags       = "";
		defines     = "";
		includes    = "";
		sourceFiles.clear();
        }

    virtual ~TaskCC()
        {}

    virtual bool execute()
        {
        DepTool depTool;
        depTool.setSourceDirectory(source);
        depTool.setFileList(sourceFiles);
        std::vector deps = depTool.getDepFile("build.dep");
        
        String incs;
        incs.append("-I");
        incs.append(parent.resolve("."));
        incs.append(" ");
        if (includes.size()>0)
            {
            incs.append(includes);
            incs.append(" ");
            }
        std::set paths;
        std::vector::iterator viter;
        for (viter=deps.begin() ; viter!=deps.end() ; viter++)
            {
            DepRec dep = *viter;
            if (dep.path.size()>0)
                paths.insert(dep.path);
            }
        if (source.size()>0)
            {
            incs.append(" -I");
            incs.append(parent.resolve(source));
            incs.append(" ");
            }
        std::set::iterator setIter;
        for (setIter=paths.begin() ; setIter!=paths.end() ; setIter++)
            {
            incs.append(" -I");
            String dname;
            if (source.size()>0)
                {
                dname.append(source);
                dname.append("/");
                }
            dname.append(*setIter);
            incs.append(parent.resolve(dname));
            }
        std::vector cfiles;
        for (viter=deps.begin() ; viter!=deps.end() ; viter++)
            {
            DepRec dep = *viter;

            //## Select command
            String sfx = dep.suffix;
            String command = ccCommand;
            if (sfx == "cpp" || sfx == "c++" || sfx == "cc"
			     || sfx == "CC")
			    command = cxxCommand;
 
            //## Make paths
            String destPath = dest;
            String srcPath  = source;
            if (dep.path.size()>0)
			    {
                destPath.append("/");
				destPath.append(dep.path);
                srcPath.append("/");
				srcPath.append(dep.path);
			    }
            //## Make sure destination directory exists
			if (!createDirectory(destPath))
			    return false;
			    
            //## Check whether it needs to be done
			String destFullName = destPath;
			destFullName.append("/");
			destFullName.append(dep.name);
			destFullName.append(".o");
			String srcFullName = srcPath;
			srcFullName.append("/");
			srcFullName.append(dep.name);
			srcFullName.append(".");
			srcFullName.append(dep.suffix);
            if (!isNewerThan(srcFullName, destFullName))
                {
                //trace("%s skipped", srcFullName.c_str());
                continue;
                }

            //## Assemble the command
            String cmd = command;
            cmd.append(" -c ");
            cmd.append(flags);
			cmd.append(" ");
            cmd.append(defines);
			cmd.append(" ");
            cmd.append(incs);
			cmd.append(" ");
			cmd.append(srcFullName);
            cmd.append(" -o ");
			cmd.append(destFullName);

            //## Execute the command

            String outString, errString;
            if (!executeCommand(cmd.c_str(), "", outString, errString))
                {
                error("problem compiling: %s", errString.c_str());
                return false;
                }
            }
        
        return true;
        }

    virtual bool parse(Element *elem)
        {
        String s;
        if (!parent.getAttribute(elem, "command", s))
            return false;
        if (s.size()>0) { ccCommand = s; cxxCommand = s; }
        if (!parent.getAttribute(elem, "cc", s))
            return false;
        if (s.size()>0) ccCommand = s;
        if (!parent.getAttribute(elem, "cxx", s))
            return false;
        if (s.size()>0) cxxCommand = s;
        if (!parent.getAttribute(elem, "destdir", s))
            return false;
        if (s.size()>0) dest = s;

        std::vector children = elem->getChildren();
        for (unsigned int i=0 ; igetName();
            if (tagName == "flags")
                {
                if (!parent.getValue(child, flags))
                    return false;
                flags = strip(flags);
                }
            else if (tagName == "includes")
                {
                if (!parent.getValue(child, includes))
                    return false;
                includes = strip(includes);
                }
            else if (tagName == "defines")
                {
                if (!parent.getValue(child, defines))
                    return false;
                defines = strip(defines);
                }
            else if (tagName == "fileset")
                {
                if (!getFileSet(child, parent, source, sourceFiles))
                    return false;
                }
            }

        return true;
        }
        
protected:

    String ccCommand;
    String cxxCommand;
    String source;
    String dest;
    String flags;
    String defines;
    String includes;
    std::vector sourceFiles;
    
};



/**
 *
 */
class TaskCopy : public Task
{
public:

    typedef enum
        {
        CP_NONE,
        CP_TOFILE,
        CP_TODIR
        } CopyType;

    TaskCopy(MakeBase &par) : Task(par)
        {
		type = TASK_COPY; name = "copy";
		cptype = CP_NONE;
		verbose = false;
		haveFileSet = false;
		}

    virtual ~TaskCopy()
        {}

    virtual bool execute()
        {
        switch (cptype)
           {
           case CP_TOFILE:
               {
               if (fileName.size()>0)
                   {
                   status("          : %s", fileName.c_str());
                   String fullSource = parent.resolve(fileName);
                   String fullDest = parent.resolve(toFileName);
                   //trace("copy %s to file %s", fullSource.c_str(),
				   //                       fullDest.c_str());
				   if (!isRegularFile(fullSource))
				       {
                       error("copy : file %s does not exist", fullSource.c_str());
				       return false;
				       }
                   if (!isNewerThan(fullSource, fullDest))
                       {
                       return true;
                       }
                   if (!copyFile(fullSource, fullDest))
                       return false;
                   status("          : 1 file copied");
                   }
               return true;
               }
           case CP_TODIR:
               {
               if (haveFileSet)
                   {
                   int nrFiles = 0;
                   status("          : %s", fileSetDir.c_str());
                   for (unsigned int i=0 ; i0)
                           {
                           sourcePath.append(fileSetDir);
                           sourcePath.append("/");
                           }
                       sourcePath.append(fileName);
                       String fullSource = parent.resolve(sourcePath);
                       
                       //Get the immediate parent directory's base name
                       String baseFileSetDir = fileSetDir;
                       unsigned int pos = baseFileSetDir.find_last_of('/');
                       if (pos!=baseFileSetDir.npos &&
					              pos < baseFileSetDir.size()-1)
                           baseFileSetDir =
						      baseFileSetDir.substr(pos+1,
							       baseFileSetDir.size());
					   //Now make the new path
                       String destPath;
                       if (toDirName.size()>0)
                           {
                           destPath.append(toDirName);
                           destPath.append("/");
                           }
                       if (baseFileSetDir.size()>0)
                           {
                           destPath.append(baseFileSetDir);
                           destPath.append("/");
                           }
                       destPath.append(fileName);
                       String fullDest = parent.resolve(destPath);
                       //trace("fileName:%s", fileName.c_str());
                       //trace("copy %s to new dir : %s", fullSource.c_str(),
				       //                   fullDest.c_str());
                       if (!isNewerThan(fullSource, fullDest))
                           {
                           //trace("copy skipping %s", fullSource.c_str());
                           continue;
                           }
                       if (!copyFile(fullSource, fullDest))
                           return false;
                       nrFiles++;
                       }
                   status("          : %d file(s) copied", nrFiles);
                   }
               else //file source
                   {
                   //For file->dir we want only the basename of
                   //the source appended to the dest dir
                   status("          : %s", fileName.c_str());
                   String baseName = fileName;
                   unsigned int pos = baseName.find_last_of('/');
                   if (pos!=baseName.npos && pos0)
                       {
                       destPath.append(toDirName);
                       destPath.append("/");
                       }
                   destPath.append(baseName);
                   String fullDest = parent.resolve(destPath);
                   //trace("copy %s to new dir : %s", fullSource.c_str(),
				   //                       fullDest.c_str());
				   if (!isRegularFile(fullSource))
				       {
                       error("copy : file %s does not exist", fullSource.c_str());
				       return false;
				       }
                   if (!isNewerThan(fullSource, fullDest))
                       {
                       return true;
                       }
                   if (!copyFile(fullSource, fullDest))
                       return false;
                   status("          : 1 file copied");
                   }
               return true;
               }
           }
        return true;
        }


    virtual bool parse(Element *elem)
        {
        if (!parent.getAttribute(elem, "file", fileName))
            return false;
        if (!parent.getAttribute(elem, "tofile", toFileName))
            return false;
        if (toFileName.size() > 0)
            cptype = CP_TOFILE;
        if (!parent.getAttribute(elem, "todir", toDirName))
            return false;
        if (toDirName.size() > 0)
            cptype = CP_TODIR;
        String ret;
        if (!parent.getAttribute(elem, "verbose", ret))
            return false;
        if (ret.size()>0 && !getBool(ret, verbose))
            return false;
            
        haveFileSet = false;
        
        std::vector children = elem->getChildren();
        for (unsigned int i=0 ; igetName();
            if (tagName == "fileset")
                {
                if (!getFileSet(child, parent, fileSetDir, fileSet))
                    {
                    error("problem getting fileset");
					return false;
					}
				haveFileSet = true;
                }
            }

        //Perform validity checks
		if (fileName.size()>0 && fileSet.size()>0)
		    {
		    error(" can only have one of : file= and ");
		    return false;
		    }
        if (toFileName.size()>0 && toDirName.size()>0)
            {
            error(" can only have one of : tofile= or todir=");
            return false;
            }
        if (haveFileSet && toDirName.size()==0)
            {
            error("a  task with a  must have : todir=");
            return false;
            }
		if (cptype == CP_TOFILE && fileName.size()==0)
		    {
		    error(" tofile= must be associated with : file=");
		    return false;
		    }
		if (cptype == CP_TODIR && fileName.size()==0 && !haveFileSet)
		    {
		    error(" todir= must be associated with : file= or ");
		    return false;
		    }

        return true;
        }
        
private:

    int cptype;
    String fileName;
    String fileSetDir;
    std::vector fileSet;
    String toFileName;
    String toDirName;
    bool verbose;
    bool haveFileSet;
};


/**
 *
 */
class TaskDelete : public Task
{
public:

    typedef enum
        {
        DEL_FILE,
        DEL_DIR,
        DEL_FILESET
        } DeleteType;

    TaskDelete(MakeBase &par) : Task(par)
        { 
		  type        = TASK_DELETE;
		  name        = "delete";
		  delType     = DEL_FILE;
          verbose     = false;
          quiet       = false;
          failOnError = true;
		}

    virtual ~TaskDelete()
        {}

    virtual bool execute()
        {
        struct stat finfo;
        switch (delType)
            {
            case DEL_FILE:
                {
                status("          : %s", fileName.c_str());
                String fullName = parent.resolve(fileName);
                char *fname = (char *)fullName.c_str();
                //does not exist
                if (stat(fname, &finfo)<0)
                    return true;
                //exists but is not a regular file
                if (!S_ISREG(finfo.st_mode))
                    {
                    error(" failed. '%s' exists and is not a regular file",
                          fname);
                    return false;
                    }
                if (remove(fname)<0)
                    {
                    error(" failed: %s", strerror(errno));
                    return false;
                    }
                return true;
                }
            case DEL_DIR:
                {
                status("          : %s", dirName.c_str());
                String fullDir = parent.resolve(dirName);
                if (!removeDirectory(fullDir))
                    return false;
                return true;
                }
            }
        return true;
        }

    virtual bool parse(Element *elem)
        {
        if (!parent.getAttribute(elem, "file", fileName))
            return false;
        if (fileName.size() > 0)
            delType = DEL_FILE;
        if (!parent.getAttribute(elem, "dir", dirName))
            return false;
        if (dirName.size() > 0)
            delType = DEL_DIR;
        if (fileName.size()>0 && dirName.size()>0)
            {
            error(" can only have one attribute of file= or dir=");
            return false;
            }
        String ret;
        if (!parent.getAttribute(elem, "verbose", ret))
            return false;
        if (ret.size()>0 && !getBool(ret, verbose))
            return false;
        if (!parent.getAttribute(elem, "quiet", ret))
            return false;
        if (ret.size()>0 && !getBool(ret, quiet))
            return false;
        if (!parent.getAttribute(elem, "failonerror", ret))
            return false;
        if (ret.size()>0 && !getBool(ret, failOnError))
            return false;
        return true;
        }

private:

    int delType;
    String dirName;
    String fileName;
    bool verbose;
    bool quiet;
    bool failOnError;
};


/**
 *
 */
class TaskJar : public Task
{
public:

    TaskJar(MakeBase &par) : Task(par)
        { type = TASK_JAR; name = "jar"; }

    virtual ~TaskJar()
        {}

    virtual bool execute()
        {
        return true;
        }

    virtual bool parse(Element *elem)
        {
        return true;
        }
};


/**
 *
 */
class TaskJavac : public Task
{
public:

    TaskJavac(MakeBase &par) : Task(par)
        { type = TASK_JAVAC; name = "javac"; }

    virtual ~TaskJavac()
        {}

    virtual bool execute()
        {
        return true;
        }

    virtual bool parse(Element *elem)
        {
        return true;
        }
};


/**
 *
 */
class TaskLink : public Task
{
public:

    TaskLink(MakeBase &par) : Task(par)
        {
		type = TASK_LINK; name = "link";
		command = "g++";
		}

    virtual ~TaskLink()
        {}

    virtual bool execute()
        {
        bool doit = false;
        String fullTarget = parent.resolve(fileName);
        String cmd = command;
        cmd.append(" -o ");
        cmd.append(fullTarget);
        cmd.append(" ");
        cmd.append(flags);
        for (unsigned int i=0 ; i0)
			    {
				obj.append(fileSetDir);
                obj.append("/");
                }
            obj.append(fileSet[i]);
            String fullObj = parent.resolve(obj);
            cmd.append(fullObj);
            if (isNewerThan(fullObj, fullTarget))
                doit = true;
            }
        cmd.append(" ");
        cmd.append(libs);
        if (!doit)
            {
            //trace("link not needed");
            return true;
            }
        //trace("LINK cmd:%s", cmd.c_str());


        String outString, errString;
        if (!executeCommand(cmd.c_str(), "", outString, errString))
            {
            error("LINK problem: %s", errString.c_str());
            return false;
            }
        return true;
        }

    virtual bool parse(Element *elem)
        {
        if (!parent.getAttribute(elem, "command", command))
            return false;
        if (!parent.getAttribute(elem, "out", fileName))
            return false;
            
        std::vector children = elem->getChildren();
        for (unsigned int i=0 ; igetName();
            if (tagName == "fileset")
                {
                if (!getFileSet(child, parent, fileSetDir, fileSet))
                    return false;
                }
            else if (tagName == "flags")
                {
                if (!parent.getValue(child, flags))
                    return false;
                flags = strip(flags);
                }
            else if (tagName == "libs")
                {
                if (!parent.getValue(child, libs))
                    return false;
                libs = strip(libs);
                }
            }
        return true;
        }

private:

    String command;
    String fileName;
    String flags;
    String libs;
    String fileSetDir;
    std::vector fileSet;

};



/**
 * Create a named directory
 */
class TaskMakeFile : public Task
{
public:

    TaskMakeFile(MakeBase &par) : Task(par)
        { type = TASK_MAKEFILE; name = "makefile"; }

    virtual ~TaskMakeFile()
        {}

    virtual bool execute()
        {
        status("          : %s", fileName.c_str());
        String fullName = parent.resolve(fileName);
        if (!isNewerThan(parent.getURI().getPath(), fullName))
            {
            //trace("skipped ");
            return true;
            }
        //trace("fullName:%s", fullName.c_str());
        FILE *f = fopen(fullName.c_str(), "w");
        if (!f)
            {
            error(" could not open %s for writing : %s",
                fullName.c_str(), strerror(errno));
            return false;
            }
        for (unsigned int i=0 ; i requires 'file=\"filename\"' attribute");
            return false;
            }
        if (!parent.getValue(elem, text))
            return false;
        text = leftJustify(text);
        //trace("dirname:%s", dirName.c_str());
        return true;
        }

private:

    String fileName;
    String text;
};



/**
 * Create a named directory
 */
class TaskMkDir : public Task
{
public:

    TaskMkDir(MakeBase &par) : Task(par)
        { type = TASK_MKDIR; name = "mkdir"; }

    virtual ~TaskMkDir()
        {}

    virtual bool execute()
        {
        status("          : %s", dirName.c_str());
        String fullDir = parent.resolve(dirName);
        //trace("fullDir:%s", fullDir.c_str());
        if (!createDirectory(fullDir))
            return false;
        return true;
        }

    virtual bool parse(Element *elem)
        {
        if (!parent.getAttribute(elem, "dir", dirName))
            return false;
        if (dirName.size() == 0)
            {
            error(" requires 'dir=\"dirname\"' attribute");
            return false;
            }
        //trace("dirname:%s", dirName.c_str());
        return true;
        }

private:

    String dirName;
};



/**
 * Create a named directory
 */
class TaskMsgFmt: public Task
{
public:

    TaskMsgFmt(MakeBase &par) : Task(par)
         {
		 type = TASK_MSGFMT;
		 name = "msgfmt";
		 command = "msgfmt";
		 }

    virtual ~TaskMsgFmt()
        {}

    virtual bool execute()
        {
        //trace("msgfmt: %d", fileSet.size());
        for (unsigned int i=0 ; i0)
			    {
			    sourcePath.append(fileSetDir);
                sourcePath.append("/");
                }
            sourcePath.append(fileName);
            String fullSource = parent.resolve(sourcePath);

            String destPath;
			if (toDirName.size()>0)
			    {
			    destPath.append(toDirName);
                destPath.append("/");
                }
            destPath.append(fileName);
            destPath[destPath.size()-2] = 'm';
            String fullDest = parent.resolve(destPath);

            if (!isNewerThan(fullSource, fullDest))
                {
                //trace("skip %s", fullSource.c_str());
                continue;
                }
                
            String cmd = command;
            cmd.append(" ");
            cmd.append(fullSource);
            cmd.append(" -o ");
            cmd.append(fullDest);
            
            int pos = fullDest.find_last_of('/');
            if (pos>0)
                {
                String fullDestPath = fullDest.substr(0, pos);
                if (!createDirectory(fullDestPath))
                    return false;
                }



            String outString, errString;
            if (!executeCommand(cmd.c_str(), "", outString, errString))
                {
                error(" problem: %s", errString.c_str());
                return false;
                }
            }

        return true;
        }

    virtual bool parse(Element *elem)
        {
        if (!parent.getAttribute(elem, "todir", toDirName))
            return false;
            
        std::vector children = elem->getChildren();
        for (unsigned int i=0 ; igetName();
            if (tagName == "fileset")
                {
                if (!getFileSet(child, parent, fileSetDir, fileSet))
                    return false;
                }
            }
        return true;
        }

private:

    String command;
    String toDirName;
    String fileSetDir;
    std::vector fileSet;

};





/**
 *  Process an archive to allow random access
 */
class TaskRanlib : public Task
{
public:

    TaskRanlib(MakeBase &par) : Task(par)
        { type = TASK_RANLIB; name = "ranlib"; }

    virtual ~TaskRanlib()
        {}

    virtual bool execute()
        {
        String fullName = parent.resolve(fileName);
        //trace("fullDir:%s", fullDir.c_str());
        String cmd = "ranlib ";
        cmd.append(fullName);
        String outbuf, errbuf;
        if (!executeCommand(cmd, "", outbuf, errbuf))
            return false;
        return true;
        }

    virtual bool parse(Element *elem)
        {
        if (!parent.getAttribute(elem, "file", fileName))
            return false;
        if (fileName.size() == 0)
            {
            error(" requires 'file=\"fileNname\"' attribute");
            return false;
            }
        return true;
        }

private:

    String fileName;
};



/**
 * Run the "ar" command to archive .o's into a .a
 */
class TaskRC : public Task
{
public:

    TaskRC(MakeBase &par) : Task(par)
        {
		type = TASK_RC; name = "rc";
		command = "windres -o";
		}

    virtual ~TaskRC()
        {}

    virtual bool execute()
        {
        String fullFile = parent.resolve(fileName);
        String fullOut  = parent.resolve(outName);
        if (!isNewerThan(fullFile, fullOut))
            return true;
        String cmd = command;
        cmd.append(" ");
        cmd.append(fullOut);
        cmd.append(" ");
        cmd.append(flags);
        cmd.append(" ");
        cmd.append(fullFile);

        String outString, errString;
        if (!executeCommand(cmd.c_str(), "", outString, errString))
            {
            error("RC problem: %s", errString.c_str());
            return false;
            }
        return true;
        }

    virtual bool parse(Element *elem)
        {
        if (!parent.getAttribute(elem, "command", command))
            return false;
        if (!parent.getAttribute(elem, "file", fileName))
            return false;
        if (!parent.getAttribute(elem, "out", outName))
            return false;
        std::vector children = elem->getChildren();
        for (unsigned int i=0 ; igetName();
            if (tagName == "flags")
                {
                if (!parent.getValue(child, flags))
                    return false;
                }
            }
        return true;
        }

private:

    String command;
    String flags;
    String fileName;
    String outName;

};



/**
 * Strip an executable
 */
class TaskStrip : public Task
{
public:

    TaskStrip(MakeBase &par) : Task(par)
        { type = TASK_STRIP; name = "strip"; }

    virtual ~TaskStrip()
        {}

    virtual bool execute()
        {
        String fullName = parent.resolve(fileName);
        //trace("fullDir:%s", fullDir.c_str());
        String cmd = "strip ";
        cmd.append(fullName);

        String outbuf, errbuf;
        if (!executeCommand(cmd, "", outbuf, errbuf))
            return false;
        return true;
        }

    virtual bool parse(Element *elem)
        {
        if (!parent.getAttribute(elem, "file", fileName))
            return false;
        if (fileName.size() == 0)
            {
            error(" requires 'file=\"fileNname\"' attribute");
            return false;
            }
        return true;
        }

private:

    String fileName;
};


/**
 *
 */
class TaskTstamp : public Task
{
public:

    TaskTstamp(MakeBase &par) : Task(par)
        { type = TASK_TSTAMP; name = "tstamp"; }

    virtual ~TaskTstamp()
        {}

    virtual bool execute()
        {
        return true;
        }

    virtual bool parse(Element *elem)
        {
        trace("tstamp parse");
        return true;
        }
};



/**
 *
 */
Task *Task::createTask(Element *elem)
{
    String tagName = elem->getName();
    //trace("task:%s", tagName.c_str());
    Task *task = NULL;
    if (tagName == "ar")
        task = new TaskAr(parent);
    else if (tagName == "cc")
        task = new TaskCC(parent);
    else if (tagName == "copy")
        task = new TaskCopy(parent);
    else if (tagName == "delete")
        task = new TaskDelete(parent);
    else if (tagName == "jar")
        task = new TaskJar(parent);
    else if (tagName == "javac")
        task = new TaskJavac(parent);
    else if (tagName == "link")
        task = new TaskLink(parent);
    else if (tagName == "makefile")
        task = new TaskMakeFile(parent);
    else if (tagName == "mkdir")
        task = new TaskMkDir(parent);
    else if (tagName == "msgfmt")
        task = new TaskMsgFmt(parent);
    else if (tagName == "ranlib")
        task = new TaskRanlib(parent);
    else if (tagName == "rc")
        task = new TaskRC(parent);
    else if (tagName == "strip")
        task = new TaskStrip(parent);
    else if (tagName == "tstamp")
        task = new TaskTstamp(parent);
    else
        {
        error("Unknown task '%s'", tagName.c_str());
        return NULL;
        }

    if (!task->parse(elem))
        {
        delete task;
        return NULL;
        }
    return task;
}



//########################################################################
//# T A R G E T
//########################################################################

/**
 *
 */
class Target : public MakeBase
{

public:

    /**
     *
     */
    Target(Make &par) : parent(par)
        { init(); }

    /**
     *
     */
    Target(const Target &other) : parent(other.parent)
        { init(); assign(other); }

    /**
     *
     */
    Target &operator=(const Target &other)
        { init(); assign(other); return *this; }

    /**
     *
     */
    virtual ~Target()
        { cleanup() ; }


    /**
     *
     */
    virtual Make &getParent()
        { return parent; }

    /**
     *
     */
    virtual String getName()
        { return name; }

    /**
     *
     */
    virtual void setName(const String &val)
        { name = val; }

    /**
     *
     */
    virtual String getDescription()
        { return description; }

    /**
     *
     */
    virtual void setDescription(const String &val)
        { description = val; }

    /**
     *
     */
    virtual void addDependency(const String &val)
        { deps.push_back(val); }

    /**
     *
     */
    virtual void parseDependencies(const String &val)
        { deps = tokenize(val, ", "); }

    /**
     *
     */
    virtual std::vector &getDependencies()
        { return deps; }

    /**
     *
     */
    virtual String getIf()
        { return ifVar; }

    /**
     *
     */
    virtual void setIf(const String &val)
        { ifVar = val; }

    /**
     *
     */
    virtual String getUnless()
        { return unlessVar; }

    /**
     *
     */
    virtual void setUnless(const String &val)
        { unlessVar = val; }

    /**
     *
     */
    virtual void addTask(Task *val)
        { tasks.push_back(val); }

    /**
     *
     */
    virtual std::vector &getTasks()
        { return tasks; }

private:

    void init()
        {
        }

    void cleanup()
        {
        tasks.clear();
        }

    void assign(const Target &other)
        {
        //parent      = other.parent;
        name        = other.name;
        description = other.description;
        ifVar       = other.ifVar;
        unlessVar   = other.unlessVar;
        deps        = other.deps;
        tasks       = other.tasks;
        }

    Make &parent;

    String name;

    String description;

    String ifVar;

    String unlessVar;

    std::vector deps;

    std::vector tasks;

};








//########################################################################
//# M A K E
//########################################################################


/**
 *
 */
class Make : public MakeBase
{

public:

    /**
     *
     */
    Make()
        { init(); }

    /**
     *
     */
    Make(const Make &other)
        { assign(other); }

    /**
     *
     */
    Make &operator=(const Make &other)
        { assign(other); return *this; }

    /**
     *
     */
    virtual ~Make()
        { cleanup(); }

    /**
     *
     */
    virtual std::map &getTargets()
        { return targets; }


    /**
     *
     */
    bool run();

    /**
     *
     */
    bool run(const String &target);



private:

    /**
     *
     */
    void init();

    /**
     *
     */
    void cleanup();

    /**
     *
     */
    void assign(const Make &other);

    /**
     *
     */
    bool executeTask(Task &task);


    /**
     *
     */
    bool executeTarget(Target &target,
	         std::set &targetsCompleted);


    /**
     *
     */
    bool execute();

    /**
     *
     */
    bool checkTargetDependencies(Target &prop,
                    std::vector &depList);

    /**
     *
     */
    bool parsePropertyFile(const String &fileName,
	                       const String &prefix);

    /**
     *
     */
    bool parseProperty(Element *elem);

    /**
     *
     */
    bool parseTask(Task &task, Element *elem);

    /**
     *
     */
    bool parseFile();

    /**
     *
     */
    std::vector glob(const String &pattern);


    //###############
    //# Fields
    //###############

    String projectName;

    String currentTarget;

    String defaultTarget;

    String specifiedTarget;

    String baseDir;

    String description;
    
    String envAlias;

    //std::vector properties;
    
    std::map targets;

    std::vector allTasks;


};


//########################################################################
//# C L A S S  M A I N T E N A N C E
//########################################################################

/**
 *
 */
void Make::init()
{
    uri             = "build.xml";
    projectName     = "";
    currentTarget   = "";
    defaultTarget   = "";
    specifiedTarget = "";
    baseDir         = "";
    description     = "";
    envAlias        = "";
    properties.clear();
    for (unsigned int i = 0 ; i < allTasks.size() ; i++)
        delete allTasks[i];
    allTasks.clear();
}



/**
 *
 */
void Make::cleanup()
{
    for (unsigned int i = 0 ; i < allTasks.size() ; i++)
        delete allTasks[i];
    allTasks.clear();
}



/**
 *
 */
void Make::assign(const Make &other)
{
    uri              = other.uri;
    projectName      = other.projectName;
    currentTarget    = other.currentTarget;
    defaultTarget    = other.defaultTarget;
    specifiedTarget  = other.specifiedTarget;
    baseDir          = other.baseDir;
    description      = other.description;
    properties       = other.properties;
}



//########################################################################
//# U T I L I T Y    T A S K S
//########################################################################

/**
 *  Perform a file globbing
 */
std::vector Make::glob(const String &pattern)
{
    std::vector res;
    return res;
}


//########################################################################
//# P U B L I C    A P I
//########################################################################



/**
 *
 */
bool Make::executeTarget(Target &target,
             std::set &targetsCompleted)
{

    String name = target.getName();

    //First get any dependencies for this target
    std::vector deps = target.getDependencies();
    for (unsigned int i=0 ; i &tgts =
               target.getParent().getTargets();
        std::map::iterator iter =
               tgts.find(dep);
        if (iter == tgts.end())
            {
            error("Target '%s' dependency '%s' not found",
                      name.c_str(),  dep.c_str());
            return false;
            }
        Target depTarget = iter->second;
        if (!executeTarget(depTarget, targetsCompleted))
            {
            return false;
            }
        }

    status("## Target : %s", name.c_str());

    //Now let's do the tasks
    std::vector &tasks = target.getTasks();
    for (unsigned int i=0 ; igetName().c_str());
        if (!task->execute())
            {
            return false;
            }
        }
        
    targetsCompleted.insert(name);
    
    return true;
}



/**
 *  Main execute() method.  Start here and work
 *  up the dependency tree 
 */
bool Make::execute()
{
    status("######## EXECUTE");

    //Determine initial target
    if (specifiedTarget.size()>0)
        {
        currentTarget = specifiedTarget;
        }
    else if (defaultTarget.size()>0)
        {
        currentTarget = defaultTarget;
        }
    else
        {
        error("execute: no specified or default target requested");
        return false;
        }

    std::map::iterator iter =
	           targets.find(currentTarget);
    if (iter == targets.end())
        {
        error("Initial target '%s' not found",
		         currentTarget.c_str());
        return false;
        }
        
    //Now run
    Target target = iter->second;
    std::set targetsCompleted;
    if (!executeTarget(target, targetsCompleted))
        {
        return false;
        }

    status("######## EXECUTE COMPLETE");
    return true;
}




/**
 *
 */
bool Make::checkTargetDependencies(Target &target, 
                            std::vector &depList)
{
    String tgtName = target.getName().c_str();
    depList.push_back(tgtName);

    std::vector deps = target.getDependencies();
    for (unsigned int i=0 ; i::iterator diter;
            for (diter=depList.begin() ; diter!=depList.end() ; diter++)
                {
                error("  %s", diter->c_str());
                }
            return false;
            }

        std::map &tgts =
                  target.getParent().getTargets();
        std::map::iterator titer = tgts.find(dep);
        if (titer == tgts.end())
            {
            error("Target '%s' dependency '%s' not found",
                      tgtName.c_str(), dep.c_str());
            return false;
            }
        if (!checkTargetDependencies(titer->second, depList))
            {
            return false;
            }
        }
    return true;
}





static int getword(int pos, const String &inbuf, String &result)
{
    int p = pos;
    int len = (int)inbuf.size();
    String val;
    while (p < len)
        {
        char ch = inbuf[p];
        if (!isalnum(ch) && ch!='.' && ch!='_')
            break;
        val.push_back(ch);
        p++;
        }
    result = val;
    return p;
}




/**
 *
 */
bool Make::parsePropertyFile(const String &fileName,
                             const String &prefix)
{
    FILE *f = fopen(fileName.c_str(), "r");
    if (!f)
        {
        error("could not open property file %s", fileName.c_str());
        return false;
        }
    int linenr = 0;
    while (!feof(f))
        {
        char buf[256];
        if (!fgets(buf, 255, f))
            break;
        linenr++;
        String s = buf;
        s = trim(s);
        int len = s.size();
        if (len == 0)
            continue;
        if (s[0] == '#')
            continue;
        String key;
        String val;
        int p = 0;
        int p2 = getword(p, s, key);
        if (p2 <= p)
            {
            error("property file %s, line %d: expected keyword",
			        fileName.c_str(), linenr);
			return false;
			}
		if (prefix.size() > 0)
		    {
		    key.insert(0, prefix);
		    }

        //skip whitespace
		for (p=p2 ; p=len || s[p]!='=')
            {
            error("property file %s, line %d: expected '='",
			        fileName.c_str(), linenr);
            return false;
            }
        p++;

        //skip whitespace
		for ( ; p=len)
            {
            error("property file %s, line %d: expected value",
			        fileName.c_str(), linenr);
			return false;
			}
        val = s.substr(p);
		if (key.size()==0 || val.size()==0)
		    continue;

        //trace("key:'%s' val:'%s'", key.c_str(), val.c_str());
	    properties[key] = val;
        }
    fclose(f);
    return true;
}




/**
 *
 */
bool Make::parseProperty(Element *elem)
{
    std::vector &attrs = elem->getAttributes();
    for (unsigned int i=0 ; i 0)
                {
                properties[attrVal] = val;
                continue;
                }
            if (!getAttribute(elem, "location", val))
                return false;
            if (val.size() > 0)
                {
                //TODO:  process a path relative to build.xml
                properties[attrVal] = val;
                continue;
                }
            }
        else if (attrName == "file")
            {
            String prefix;
			if (!getAttribute(elem, "prefix", prefix))
			    return false;
            if (prefix.size() > 0)
                {
                if (prefix[prefix.size()-1] != '.')
                    prefix.push_back('.');
                }
            if (!parsePropertyFile(attrName, prefix))
                return false;
            }
        else if (attrName == "environment")
            {
            if (envAlias.size() > 0)
                {
                error("environment property can only be set once");
                return false;
                }
            envAlias = attrVal;
            }
        }

    return true;
}




/**
 *
 */
bool Make::parseFile()
{
    status("######## PARSE");

    Parser parser;
    Element *root = parser.parseFile(uri.getNativePath());
    if (!root)
        {
        error("Could not open %s for reading",
		      uri.getNativePath().c_str());
        return false;
        }

    if (root->getChildren().size()==0 ||
        root->getChildren()[0]->getName()!="project")
        {
        error("Main xml element should be ");
        delete root;
        return false;
        }

    //########## Project attributes
    Element *project = root->getChildren()[0];
    String s = project->getAttribute("name");
    if (s.size() > 0)
        projectName = s;
    s = project->getAttribute("default");
    if (s.size() > 0)
        defaultTarget = s;
    s = project->getAttribute("basedir");
    if (s.size() > 0)
        baseDir = s;

    //######### PARSE MEMBERS
    std::vector children = project->getChildren();
    for (unsigned int i=0 ; igetName();

        //########## DESCRIPTION
        if (tagName == "description")
            {
            description = parser.trim(elem->getValue());
            }

        //######### PROPERTY
        else if (tagName == "property")
            {
            if (!parseProperty(elem))
                return false;
            }

        //######### TARGET
        else if (tagName == "target")
            {
            String tname   = elem->getAttribute("name");
            String tdesc   = elem->getAttribute("description");
            String tdeps   = elem->getAttribute("depends");
            String tif     = elem->getAttribute("if");
            String tunless = elem->getAttribute("unless");
            Target target(*this);
            target.setName(tname);
            target.setDescription(tdesc);
            target.parseDependencies(tdeps);
            target.setIf(tif);
            target.setUnless(tunless);
            std::vector telems = elem->getChildren();
            for (unsigned int i=0 ; i(tname, target));
            }

        }

    std::map::iterator iter;
    for (iter = targets.begin() ; iter!= targets.end() ; iter++)
        {
        Target tgt = iter->second;
        std::vector depList;
        if (!checkTargetDependencies(tgt, depList))
            {
            return false;
            }
        }


    delete root;
    status("######## PARSE COMPLETE");
    return true;
}


/**
 *
 */
bool Make::run()
{
    if (!parseFile())
        return false;
    if (!execute())
        return false;
    return true;
}


/**
 *
 */
bool Make::run(const String &target)
{
    status("##################################");
    status("#   BuildTool");
    status("#   version 0.2");
    status("##################################");
    specifiedTarget = target;
    if (!run())
        return false;
    status("##################################");
    status("#   BuildTool Completed");
    status("##################################");
    return true;
}







}// namespace buildtool
//########################################################################
//# M A I N
//########################################################################

/**
 *  Format an error message in printf() style
 */
static void error(char *fmt, ...)
{
    va_list ap;
    va_start(ap, fmt);
    fprintf(stderr, "BuildTool error: ");
    vfprintf(stderr, fmt, ap);
    fprintf(stderr, "\n");
    va_end(ap);
}


/**
 * Compare a buffer with a key, for the length of the key
 */
static bool sequ(const buildtool::String &buf, char *key)
{
    for (int i=0 ; key[i] ; i++)
        {
        if (key[i] != buf[i])
            return false;
        }        
    return true;
}

/**
 * Parse the command-line args, get our options,
 * and run this thing
 */   
static bool parseOptions(int argc, char **argv)
{
    if (argc < 1)
        {
        error("Cannot parse arguments");
        return false;
        }

    buildtool::String buildFile;
    buildtool::String target;

    //char *progName = argv[0];
    for (int i=1 ; i 0)
        {
        make.setURI(buildFile);
        }
    if (!make.run(target))
        return false;

    return true;
}




/*
static bool runMake()
{
    buildtool::Make make;
    if (!make.run())
        return false;
    return true;
}


static bool pkgConfigTest()
{
    buildtool::PkgConfig pkgConfig;
    if (!pkgConfig.readFile("gtk+-2.0.pc"))
        return false;
    return true;
}



static bool depTest()
{
    buildtool::DepTool deptool;
    deptool.setSourceDirectory("/dev/ink/inkscape/src");
    if (!deptool.generateDependencies("build.dep"))
        return false;
    std::vector res =
	       deptool.loadDepFile("build.dep");
	if (res.size() == 0)
        return false;
    return true;
}

static bool popenTest()
{
    buildtool::Make make;
    buildtool::String out, err;
	bool ret = make.executeCommand("gcc xx.cpp", "", out, err);
    printf("Popen test:%d '%s' '%s'\n", ret, out.c_str(), err.c_str());
    return true;
}


static bool propFileTest()
{
    buildtool::Make make;
    make.parsePropertyFile("test.prop", "test.");
    return true;
}
*/

int main(int argc, char **argv)
{

    if (!parseOptions(argc, argv))
        return 1;
    /*
    if (!popenTest())
        return 1;

    if (!depTest())
        return 1;
    if (!propFileTest())
        return 1;
    if (runMake())
        return 1;
    */
    return 0;
}


//########################################################################
//# E N D 
//########################################################################