/**
 * Simple build automation tool.
 *
 * Authors:
 *   Bob Jamison
 *   Jasper van de Gronde
 *   Johan Engelen
 *
 * Copyright (C) 2006-2008 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:
 * <pre>
 * g++ -O3 buildtool.cpp -o btool.exe -fopenmp
 * (or whatever your compiler might be)
 * Then
 * btool
 * or
 * btool {target}
 *
 * Note: if you are using MinGW, and a not very recent version of it,
 * gettimeofday() might be missing.  If so, just build this file with
 * this command:
 * g++ -O3 -DNEED_GETTIMEOFDAY buildtool.cpp -o btool.exe -fopenmp
 *
 */

#define BUILDTOOL_VERSION  "BuildTool v0.9.9multi"

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdarg.h>
#include <sys/stat.h>
#include <time.h>
#include <sys/time.h>
#include <utime.h>
#include <dirent.h>

#include <iostream>
#include <list>
#include <string>
#include <map>
#include <set>
#include <vector>
#include <algorithm>


#ifdef __WIN32__
#define WIN32_LEAN_AND_MEAN
#define NOGDI
#include <windows.h>
#endif

#include <errno.h>


//########################################################################
//# Definition of gettimeofday() for those who don't have it
//########################################################################
#ifdef NEED_GETTIMEOFDAY
#include <sys/timeb.h>

struct timezone {
      int tz_minuteswest; /* minutes west of Greenwich */
      int tz_dsttime;     /* type of dst correction */
    };

static int gettimeofday (struct timeval *tv, struct timezone *tz)
{
   struct _timeb tb;

   if (!tv)
      return (-1);

    _ftime (&tb);
    tv->tv_sec  = tb.time;
    tv->tv_usec = tb.millitm * 1000 + 500;
    if (tz)
        {
        tz->tz_minuteswest = -60 * _timezone;
        tz->tz_dsttime = _daylight;
        }
    return 0;
}

#endif







namespace buildtool
{




//########################################################################
//########################################################################
//##  R E G E X P
//########################################################################
//########################################################################

/**
 * This is the SLRE (Super Light Regular Expression library)
 * SLRE is an ISO C library that implements a subset of Perl
 * regular expression syntax.
 *
 * See https://github.com/cesanta/slre for details
 *
 * It's clean code and small size allow us to
 * embed it in BuildTool without adding a dependency
 *
 */    

//begin slre.h

/*
 * Copyright (c) 2004-2013 Sergey Lyubka <valenok@gmail.com>
 * Copyright (c) 2013 Cesanta Software Limited
 * All rights reserved
 *
 * This library is dual-licensed: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation. For the terms of this
 * license, see <http://www.gnu.org/licenses/>.
 *
 * You are free to use this library under the terms of the GNU General
 * Public License, but WITHOUT ANY WARRANTY; without even the implied
 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 *
 * Alternatively, you can license this library under a commercial
 * license, as set out in <http://cesanta.com/products.html>.
 */

/*
 * This is a regular expression library that implements a subset of Perl RE.
 * Please refer to README.md for a detailed reference.
 */

#ifndef SLRE_HEADER_DEFINED
#define SLRE_HEADER_DEFINED

#ifdef __cplusplus
extern "C" {
#endif

struct slre_cap {
  const char *ptr;
  int len;
};


int slre_match(const char *regexp, const char *buf, int buf_len,
               struct slre_cap *caps, int num_caps, int flags);

/* Possible flags for slre_match() */
enum { SLRE_IGNORE_CASE = 1 };


/* slre_match() failure codes */
#define SLRE_NO_MATCH               -1
#define SLRE_UNEXPECTED_QUANTIFIER  -2
#define SLRE_UNBALANCED_BRACKETS    -3
#define SLRE_INTERNAL_ERROR         -4
#define SLRE_INVALID_CHARACTER_SET  -5
#define SLRE_INVALID_METACHARACTER  -6
#define SLRE_CAPS_ARRAY_TOO_SMALL   -7
#define SLRE_TOO_MANY_BRANCHES      -8
#define SLRE_TOO_MANY_BRACKETS      -9

#ifdef __cplusplus
}
#endif

#endif  /* SLRE_HEADER_DEFINED */

//end slre.h

//start slre.c

/*
 * Copyright (c) 2004-2013 Sergey Lyubka <valenok@gmail.com>
 * Copyright (c) 2013 Cesanta Software Limited
 * All rights reserved
 *
 * This library is dual-licensed: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation. For the terms of this
 * license, see <http://www.gnu.org/licenses/>.
 *
 * You are free to use this library under the terms of the GNU General
 * Public License, but WITHOUT ANY WARRANTY; without even the implied
 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 *
 * Alternatively, you can license this library under a commercial
 * license, as set out in <http://cesanta.com/products.html>.
 */

#include <stdio.h>
#include <ctype.h>
#include <string.h>

//#include "slre.h"

#define MAX_BRANCHES 100
#define MAX_BRACKETS 100
#define FAIL_IF(condition, error_code) if (condition) return (error_code)

#ifndef ARRAY_SIZE
#define ARRAY_SIZE(ar) (sizeof(ar) / sizeof((ar)[0]))
#endif

#ifdef SLRE_DEBUG
#define DBG(x) printf x
#else
#define DBG(x)
#endif

struct bracket_pair {
  const char *ptr;  /* Points to the first char after '(' in regex  */
  int len;          /* Length of the text between '(' and ')'       */
  int branches;     /* Index in the branches array for this pair    */
  int num_branches; /* Number of '|' in this bracket pair           */
};

struct branch {
  int bracket_index;    /* index for 'struct bracket_pair brackets' */
                        /* array defined below                      */
  const char *schlong;  /* points to the '|' character in the regex */
};

struct regex_info {
  /*
   * Describes all bracket pairs in the regular expression.
   * First entry is always present, and grabs the whole regex.
   */
  struct bracket_pair brackets[MAX_BRACKETS];
  int num_brackets;

  /*
   * Describes alternations ('|' operators) in the regular expression.
   * Each branch falls into a specific branch pair.
   */
  struct branch branches[MAX_BRANCHES];
  int num_branches;

  /* Array of captures provided by the user */
  struct slre_cap *caps;
  int num_caps;

  /* E.g. SLRE_IGNORE_CASE. See enum below */
  int flags;
};

static int is_metacharacter(const unsigned char *s) {
  static const char *metacharacters = "^$().[]*+?|\\Ssdbfnrtv";
  return strchr(metacharacters, *s) != NULL;
}

static int op_len(const char *re) {
  return re[0] == '\\' && re[1] == 'x' ? 4 : re[0] == '\\' ? 2 : 1;
}

static int set_len(const char *re, int re_len) {
  int len = 0;

  while (len < re_len && re[len] != ']') {
    len += op_len(re + len);
  }

  return len <= re_len ? len + 1 : -1;
}

static int get_op_len(const char *re, int re_len) {
  return re[0] == '[' ? set_len(re + 1, re_len - 1) + 1 : op_len(re);
}

static int is_quantifier(const char *re) {
  return re[0] == '*' || re[0] == '+' || re[0] == '?';
}

static int toi(int x) {
  return isdigit(x) ? x - '0' : x - 'W';
}

static int hextoi(const unsigned char *s) {
  return (toi(tolower(s[0])) << 4) | toi(tolower(s[1]));
}

static int match_op(const unsigned char *re, const unsigned char *s,
                    struct regex_info *info) {
  int result = 0;
  switch (*re) {
    case '\\':
      /* Metacharacters */
      switch (re[1]) {
        case 'S': FAIL_IF(isspace(*s), SLRE_NO_MATCH); result++; break;
        case 's': FAIL_IF(!isspace(*s), SLRE_NO_MATCH); result++; break;
        case 'd': FAIL_IF(!isdigit(*s), SLRE_NO_MATCH); result++; break;
        case 'b': FAIL_IF(*s != '\b', SLRE_NO_MATCH); result++; break;
        case 'f': FAIL_IF(*s != '\f', SLRE_NO_MATCH); result++; break;
        case 'n': FAIL_IF(*s != '\n', SLRE_NO_MATCH); result++; break;
        case 'r': FAIL_IF(*s != '\r', SLRE_NO_MATCH); result++; break;
        case 't': FAIL_IF(*s != '\t', SLRE_NO_MATCH); result++; break;
        case 'v': FAIL_IF(*s != '\v', SLRE_NO_MATCH); result++; break;

        case 'x':
          /* Match byte, \xHH where HH is hexadecimal byte representaion */
          FAIL_IF(hextoi(re + 2) != *s, SLRE_NO_MATCH);
          result++;
          break;

        default:
          /* Valid metacharacter check is done in bar() */
          FAIL_IF(re[1] != s[0], SLRE_NO_MATCH);
          result++;
          break;
      }
      break;

    case '|': FAIL_IF(1, SLRE_INTERNAL_ERROR); break;
    case '$': FAIL_IF(1, SLRE_NO_MATCH); break;
    case '.': result++; break;

    default:
      if (info->flags & SLRE_IGNORE_CASE) {
        FAIL_IF(tolower(*re) != tolower(*s), SLRE_NO_MATCH);
      } else {
        FAIL_IF(*re != *s, SLRE_NO_MATCH);
      }
      result++;
      break;
  }

  return result;
}

static int match_set(const char *re, int re_len, const char *s,
                     struct regex_info *info) {
  int len = 0, result = -1, invert = re[0] == '^';

  if (invert) re++, re_len--;

  while (len <= re_len && re[len] != ']' && result <= 0) {
    /* Support character range */
    if (re[len] != '-' && re[len + 1] == '-' && re[len + 2] != ']' &&
        re[len + 2] != '\0') {
      result = info->flags &  SLRE_IGNORE_CASE ?
        tolower(*s) >= tolower(re[len]) && tolower(*s) <= tolower(re[len + 2]) :
        *s >= re[len] && *s <= re[len + 2];
      len += 3;
    } else {
      result = match_op((unsigned char *) re + len, (unsigned char *) s, info);
      len += op_len(re + len);
    }
  }
  return (!invert && result > 0) || (invert && result <= 0) ? 1 : -1;
}

static int doh(const char *s, int s_len, struct regex_info *info, int bi);

static int bar(const char *re, int re_len, const char *s, int s_len,
               struct regex_info *info, int bi) {
  /* i is offset in re, j is offset in s, bi is brackets index */
  int i, j, n, step;

  for (i = j = 0; i < re_len && j <= s_len; i += step) {

    /* Handle quantifiers. Get the length of the chunk. */
    step = re[i] == '(' ? info->brackets[bi + 1].len + 2 :
      get_op_len(re + i, re_len - i);

    DBG(("%s [%.*s] [%.*s] re_len=%d step=%d i=%d j=%d\n", __func__,
         re_len - i, re + i, s_len - j, s + j, re_len, step, i, j));

    FAIL_IF(is_quantifier(&re[i]), SLRE_UNEXPECTED_QUANTIFIER);
    FAIL_IF(step <= 0, SLRE_INVALID_CHARACTER_SET);

    if (i + step < re_len && is_quantifier(re + i + step)) {
      DBG(("QUANTIFIER: [%.*s]%c [%.*s]\n", step, re + i,
           re[i + step], s_len - j, s + j));
      if (re[i + step] == '?') {
        int result = bar(re + i, step, s + j, s_len - j, info, bi);
        j += result > 0 ? result : 0;
        i++;
      } else if (re[i + step] == '+' || re[i + step] == '*') {
        int j2 = j, nj = j, n1, n2 = -1, ni, non_greedy = 0;

        /* Points to the regexp code after the quantifier */
        ni = i + step + 1;
        if (ni < re_len && re[ni] == '?') {
          non_greedy = 1;
          ni++;
        }

        do {
          if ((n1 = bar(re + i, step, s + j2, s_len - j2, info, bi)) > 0) {
            j2 += n1;
          }
          if (re[i + step] == '+' && n1 < 0) break;

          if (ni >= re_len) {
            /* After quantifier, there is nothing */
            nj = j2;
          } else if ((n2 = bar(re + ni, re_len - ni, s + j2,
                               s_len - j2, info, bi)) >= 0) {
            /* Regex after quantifier matched */
            nj = j2 + n2;
          }
          if (nj > j && non_greedy) break;
        } while (n1 > 0);

        /*
         * Even if we found one or more pattern, this branch will be executed,
         * changing the next captures.
         */
        if (n1 < 0 && n2 < 0 && re[i + step] == '*' &&
            (n2 = bar(re + ni, re_len - ni, s + j, s_len - j, info, bi)) > 0) {
          nj = j + n2;
        }

        DBG(("STAR/PLUS END: %d %d %d %d %d\n", j, nj, re_len - ni, n1, n2));
        FAIL_IF(re[i + step] == '+' && nj == j, SLRE_NO_MATCH);

        /* If while loop body above was not executed for the * quantifier,  */
        /* make sure the rest of the regex matches                          */
        FAIL_IF(nj == j && ni < re_len && n2 < 0, SLRE_NO_MATCH);

        /* Returning here cause we've matched the rest of RE already */
        return nj;
      }
      continue;
    }

    if (re[i] == '[') {
      n = match_set(re + i + 1, re_len - (i + 2), s + j, info);
      DBG(("SET %.*s [%.*s] -> %d\n", step, re + i, s_len - j, s + j, n));
      FAIL_IF(n <= 0, SLRE_NO_MATCH);
      j += n;
    } else if (re[i] == '(') {
      n = SLRE_NO_MATCH;
      bi++;
      FAIL_IF(bi >= info->num_brackets, SLRE_INTERNAL_ERROR);
      DBG(("CAPTURING [%.*s] [%.*s] [%s]\n",
           step, re + i, s_len - j, s + j, re + i + step));

      if (re_len - (i + step) <= 0) {
        /* Nothing follows brackets */
        n = doh(s + j, s_len - j, info, bi);
      } else {
        int j2;
        for (j2 = 0; j2 <= s_len - j; j2++) {
          if ((n = doh(s + j, s_len - (j + j2), info, bi)) >= 0 &&
              bar(re + i + step, re_len - (i + step),
                  s + j + n, s_len - (j + n), info, bi) >= 0) break;
        }
      }

      DBG(("CAPTURED [%.*s] [%.*s]:%d\n", step, re + i, s_len - j, s + j, n));
      FAIL_IF(n < 0, n);
      if (info->caps != NULL && n > 0) {
        info->caps[bi - 1].ptr = s + j;
        info->caps[bi - 1].len = n;
      }
      j += n;
    } else if (re[i] == '^') {
      FAIL_IF(j != 0, SLRE_NO_MATCH);
    } else if (re[i] == '$') {
      FAIL_IF(j != s_len, SLRE_NO_MATCH);
    } else {
      FAIL_IF(j >= s_len, SLRE_NO_MATCH);
      n = match_op((unsigned char *) (re + i), (unsigned char *) (s + j), info);
      FAIL_IF(n <= 0, n);
      j += n;
    }
  }

  return j;
}

/* Process branch points */
static int doh(const char *s, int s_len, struct regex_info *info, int bi) {
  const struct bracket_pair *b = &info->brackets[bi];
  int i = 0, len, result;
  const char *p;

  do {
    p = i == 0 ? b->ptr : info->branches[b->branches + i - 1].schlong + 1;
    len = b->num_branches == 0 ? b->len :
      i == b->num_branches ? (int) (b->ptr + b->len - p) :
      (int) (info->branches[b->branches + i].schlong - p);
    DBG(("%s %d %d [%.*s] [%.*s]\n", __func__, bi, i, len, p, s_len, s));
    result = bar(p, len, s, s_len, info, bi);
    DBG(("%s <- %d\n", __func__, result));
  } while (result <= 0 && i++ < b->num_branches);  /* At least 1 iteration */

  return result;
}

static int baz(const char *s, int s_len, struct regex_info *info) {
  int i, result = -1, is_anchored = info->brackets[0].ptr[0] == '^';

  for (i = 0; i <= s_len; i++) {
    result = doh(s + i, s_len - i, info, 0);
    if (result >= 0) {
      result += i;
      break;
    }
    if (is_anchored) break;
  }

  return result;
}

static void setup_branch_points(struct regex_info *info) {
  int i, j;
  struct branch tmp;

  /* First, sort branches. Must be stable, no qsort. Use bubble algo. */
  for (i = 0; i < info->num_branches; i++) {
    for (j = i + 1; j < info->num_branches; j++) {
      if (info->branches[i].bracket_index > info->branches[j].bracket_index) {
        tmp = info->branches[i];
        info->branches[i] = info->branches[j];
        info->branches[j] = tmp;
      }
    }
  }

  /*
   * For each bracket, set their branch points. This way, for every bracket
   * (i.e. every chunk of regex) we know all branch points before matching.
   */
  for (i = j = 0; i < info->num_brackets; i++) {
    info->brackets[i].num_branches = 0;
    info->brackets[i].branches = j;
    while (j < info->num_branches && info->branches[j].bracket_index == i) {
      info->brackets[i].num_branches++;
      j++;
    }
  }
}

static int foo(const char *re, int re_len, const char *s, int s_len,
               struct regex_info *info) {
  int i, step, depth = 0;

  /* First bracket captures everything */
  info->brackets[0].ptr = re;
  info->brackets[0].len = re_len;
  info->num_brackets = 1;

  /* Make a single pass over regex string, memorize brackets and branches */
  for (i = 0; i < re_len; i += step) {
    step = get_op_len(re + i, re_len - i);

    if (re[i] == '|') {
      FAIL_IF(info->num_branches >= (int) ARRAY_SIZE(info->branches),
              SLRE_TOO_MANY_BRANCHES);
      info->branches[info->num_branches].bracket_index =
        info->brackets[info->num_brackets - 1].len == -1 ?
        info->num_brackets - 1 : depth;
      info->branches[info->num_branches].schlong = &re[i];
      info->num_branches++;
    } else if (re[i] == '\\') {
      FAIL_IF(i >= re_len - 1, SLRE_INVALID_METACHARACTER);
      if (re[i + 1] == 'x') {
        /* Hex digit specification must follow */
        FAIL_IF(re[i + 1] == 'x' && i >= re_len - 3,
                SLRE_INVALID_METACHARACTER);
        FAIL_IF(re[i + 1] ==  'x' && !(isxdigit(re[i + 2]) &&
                isxdigit(re[i + 3])), SLRE_INVALID_METACHARACTER);
      } else {
        FAIL_IF(!is_metacharacter((unsigned char *) re + i + 1),
                SLRE_INVALID_METACHARACTER);
      }
    } else if (re[i] == '(') {
      FAIL_IF(info->num_brackets >= (int) ARRAY_SIZE(info->brackets),
              SLRE_TOO_MANY_BRACKETS);
      depth++;  /* Order is important here. Depth increments first. */
      info->brackets[info->num_brackets].ptr = re + i + 1;
      info->brackets[info->num_brackets].len = -1;
      info->num_brackets++;
      FAIL_IF(info->num_caps > 0 && info->num_brackets - 1 > info->num_caps,
              SLRE_CAPS_ARRAY_TOO_SMALL);
    } else if (re[i] == ')') {
      int ind = info->brackets[info->num_brackets - 1].len == -1 ?
        info->num_brackets - 1 : depth;
      info->brackets[ind].len = (int) (&re[i] - info->brackets[ind].ptr);
      DBG(("SETTING BRACKET %d [%.*s]\n",
           ind, info->brackets[ind].len, info->brackets[ind].ptr));
      depth--;
      FAIL_IF(depth < 0, SLRE_UNBALANCED_BRACKETS);
      FAIL_IF(i > 0 && re[i - 1] == '(', SLRE_NO_MATCH);
    }
  }

  FAIL_IF(depth != 0, SLRE_UNBALANCED_BRACKETS);
  setup_branch_points(info);

  return baz(s, s_len, info);
}

int slre_match(const char *regexp, const char *s, int s_len,
               struct slre_cap *caps, int num_caps, int flags) {
  struct regex_info info;

  /* Initialize info structure */
  info.flags = flags;
  info.num_brackets = info.num_branches = 0;
  info.num_caps = num_caps;
  info.caps = caps;

  DBG(("========================> [%s] [%.*s]\n", regexp, s_len, s));
  return foo(regexp, (int) strlen(regexp), s, s_len, &info);
}

//end slre.c

//########################################################################
//########################################################################
//##  E N D    R E G E X P
//########################################################################
//########################################################################





//########################################################################
//########################################################################
//##  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()
        {
        init();
        }

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

    Element(const String &nameArg, const String &valueArg)
        {
        init();
        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 (std::size_t i=0 ; i<children.size() ; i++)
            delete children[i];
        }

    virtual String getName()
        { return name; }

    virtual String getValue()
        { return value; }

    Element *getParent()
        { return parent; }

    std::vector<Element *> getChildren()
        { return children; }

    std::vector<Element *> findElements(const String &name);

    String getAttribute(const String &name);

    std::vector<Attribute> &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();
    
    int getLine()
        { return line; }

protected:

    void init()
        {
        parent = NULL;
        line   = 0;
        }

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

    void findElementsRecursive(std::vector<Element *>&res, const String &name);

    void writeIndentedRecursive(FILE *f, int indent);

    Element *parent;

    std::vector<Element *>children;

    std::vector<Attribute> attributes;
    std::vector<Namespace> namespaces;

    String name;
    String value;
    
    int line;
};





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;
        }

    int countLines(int begin, int end);

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

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

    int peek(int pos);

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

    int skipwhite(int 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;
    int        parselen;
    XMLCh      *parsebuf;
    String     cdatabuf;
    int        currentPosition;
};




//########################################################################
//# 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;
    elem->line       = line;

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


void Element::findElementsRecursive(std::vector<Element *>&res, const String &name)
{
    if (getName() == name)
        {
        res.push_back(this);
        }
    for (std::size_t i=0; i<children.size() ; i++)
        children[i]->findElementsRecursive(res, name);
}

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

String Element::getAttribute(const String &name)
{
    for (std::size_t i=0 ; i<attributes.size() ; i++)
        if (attributes[i].getName() ==name)
            return attributes[i].getValue();
    return "";
}

String Element::getTagAttribute(const String &tagName, const String &attrName)
{
    std::vector<Element *>elems = findElements(tagName);
    if (elems.size() <1)
        return "";
    String res = elems[0]->getAttribute(attrName);
    return res;
}

String Element::getTagValue(const String &tagName)
{
    std::vector<Element *>elems = 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<indent;i++)
        fputc(' ',f);
    fprintf(f,"<%s",name.c_str());
    for (std::size_t i=0 ; i<attributes.size() ; i++)
        {
        fprintf(f," %s=\"%s\"",
              attributes[i].getName().c_str(),
              attributes[i].getValue().c_str());
        }
    for (std::size_t i=0 ; i<namespaces.size() ; i++)
        {
        fprintf(f," xmlns:%s=\"%s\"",
              namespaces[i].getPrefix().c_str(),
              namespaces[i].getNamespaceURI().c_str());
        }
    fprintf(f,">\n");

    //Between the tags
    if (value.size() > 0)
        {
        for (int i=0;i<indent;i++)
            fputc(' ', f);
        fprintf(f," %s\n", value.c_str());
        }

    for (std::size_t i=0 ; i<children.size() ; i++)
        children[i]->writeIndentedRecursive(f, indent+2);

    //Closing tag
    for (int i=0; i<indent; i++)
        fputc(' ',f);
    fprintf(f,"</%s>\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
    {
    const char *escaped;
    char value;
    } EntityEntry;

static EntityEntry entities[] =
{
    { "&amp;" , '&'  },
    { "&lt;"  , '<'  },
    { "&gt;"  , '>'  },
    { "&apos;", '\'' },
    { "&quot;", '"'  },
    { 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
    std::size_t begin = 0;
    for ( ; begin < s.size() ; begin++)
        {
        if (!isspace(s[begin]))
            break;
        }

    //Find first non-ws char, going in reverse
    std::size_t 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;
}


int Parser::countLines(int begin, int end)
{
    int count = 0;
    for (int i=begin ; i<end ; i++)
        {
        XMLCh ch = parsebuf[i];
        if (ch == '\n' || ch == '\r')
            count++;
        }
    return count;
}


void Parser::getLineAndColumn(int pos, int *lineNr, int *colNr)
{
    int line = 1;
    int col  = 1;
    for (long i=0 ; i<pos ; i++)
        {
        XMLCh ch = parsebuf[i];
        if (ch == '\n' || ch == '\r')
            {
            col = 0;
            line ++;
            }
        else
            col++;
        }
    *lineNr = line;
    *colNr  = col;

}


void Parser::error(const char *fmt, ...)
{
    int lineNr;
    int colNr;
    getLineAndColumn(currentPosition, &lineNr, &colNr);
    va_list args;
    fprintf(stderr, "xml error at line %d, column %d:", lineNr, colNr);
    va_start(args,fmt);
    vfprintf(stderr,fmt,args);
    va_end(args) ;
    fprintf(stderr, "\n");
}



int Parser::peek(int pos)
{
    if (pos >= 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 (std::size_t i=0 ; i<str.size() ; i++)
        {
        XMLCh ch = (XMLCh)str[i];
        if (ch == '&')
            ret.append("&amp;");
        else if (ch == '<')
            ret.append("&lt;");
        else if (ch == '>')
            ret.append("&gt;");
        else if (ch == '\'')
            ret.append("&apos;");
        else if (ch == '"')
            ret.append("&quot;");
        else
            ret.push_back(ch);

        }
    return ret;
}


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



int Parser::skipwhite(int p)
{

    while (p<parselen)
        {
        int p2 = match(p, "<!--");
        if (p2 > p)
            {
            p = p2;
            while (p<parselen)
              {
              p2 = match(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<parselen)
        {
        XMLCh b = peek(p);
        if (b<=' ' || b=='/' || b=='>' || 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 ( p<parselen )
        {
        XMLCh b = peek(p);
        if (b=='"' || b=='\'')
            break;
        if (b=='&' && do_i_parse)
            {
            bool found = false;
            for (EntityEntry *ee = entities ; ee->value ; 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)
        {
        XMLCh ch = peek(p);
        if (ch=='?')
            {
            p++;
            break;
            }
        buf.push_back(ch);
        p++;
        }

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

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

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

    int p = p0;
    p = skipwhite(p);

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

    p++;

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

    String buf;
    while (p<parselen)
        {
        XMLCh ch = peek(p);
        if (ch=='>')
            {
            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 lineNr)
{

    int p = p0;

    int p2 = p;

    p = skipwhite(p);

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

    //int line, col;
    //getLineAndColumn(p, &line, &col);

    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->line = lineNr + countLines(p0, p);
    n->parent = par;
    par->addChild(n);

    // Get attributes
    if (peek(p) != '>')
        {
        while (p<parselen)
            {
            p = skipwhite(p);
            ch = peek(p);
            //printf("ch:%c\n",ch);
            if (ch=='>')
                break;
            else if (ch=='/' && p<parselen+1)
                {
                p++;
                p = skipwhite(p);
                ch = peek(p);
                if (ch=='>')
                    {
                    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 (p<parselen)
        {
        //# COMMENT
        p2 = match(p, "<!--");
        if (!cdata && p2>p)
            {
            p = p2;
            while (p<parselen)
                {
                p2 = match(p, "-->");
                if (p2 > p)
                    {
                    p = p2;
                    break;
                    }
                p++;
                }
            }

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

        //# CHILD ELEMENT
        if (ch == '<')
            {
            p2 = parseElement(p, n, lineNr + countLines(p0, p));
            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 </%S>. 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, 1);
    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, const 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;
    const 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__
    std::size_t firstChar = 0;
    if (path.size() >= 3)
        {
        if (path[0] == '/' &&
            isLetter(path[1]) &&
            path[2] == ':')
            firstChar++;
         }
    for (std::size_t i=firstChar ; i<path.size() ; i++)
        {
        XMLCh ch = (XMLCh) path[i];
        if (ch == '/')
            npath.push_back((XMLCh)'\\');
        else
            npath.push_back(ch);
        }
#else
    npath = path;
#endif
    return npath;
}


bool URI::isAbsolute() const
{
    return absolute;
}

bool URI::isOpaque() const
{
    return opaque;
}


String URI::getQuery() const
{
    return query;
}


String URI::getFragment() const
{
    return fragment;
}


URI URI::resolve(const URI &other) const
{
    //### According to w3c, this is handled in 3 cases

    //## 1
    if (opaque || other.isAbsolute())
        return other;

    //## 2
    if (other.fragment.size()  >  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
            {
            std::size_t 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<String> segments;

    //## Collect segments
    if (path.size()<2)
        return;
    bool abs = false;
    std::size_t pos=0;
    if (path[0]=='/')
        {
        abs = true;
        pos++;
        }
    while (pos < path.size())
        {
        std::size_t 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<String>::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<String>::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, const 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)
{
    init();
    
    parselen = str.size();

    String tmp;
    for (std::size_t i=0 ; i<str.size() ; i++)
        {
        XMLCh ch = (XMLCh) str[i];
        if (ch == '\\')
            tmp.push_back((XMLCh)'/');
        else
            tmp.push_back(ch);
        }
    parsebuf = (char *) tmp.c_str();


    int p = parse(0);
    normalize();

    if (p < 0)
        {
        error("Syntax error");
        return false;
        }

    //printf("uri:%s\n", toString().c_str());
    //printf("path:%s\n", path.c_str());

    return true;

}








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

//########################################################################
//# Stat cache to speed up stat requests
//########################################################################
struct StatResult {
    int result;
    struct stat statInfo;
};
typedef std::map<String, StatResult> statCacheType;
static statCacheType statCache;
static int cachedStat(const String &f, struct stat *s) {
    //printf("Stat path: %s\n", f.c_str());
    std::pair<statCacheType::iterator, bool> result = statCache.insert(statCacheType::value_type(f, StatResult()));
    if (result.second) {
        result.first->second.result = stat(f.c_str(), &(result.first->second.statInfo));
    }
    *s = result.first->second.statInfo;
    return result.first->second.result;
}
static void removeFromStatCache(const String f) {
    //printf("Removing from cache: %s\n", f.c_str());
    statCache.erase(f);
}

//########################################################################
//# Dir cache to speed up dir requests
//########################################################################
/*struct DirListing {
    bool available;
    std::vector<String> files;
    std::vector<String> dirs;
};
typedef std::map<String, DirListing > dirCacheType;
static dirCacheType dirCache;
static const DirListing &cachedDir(String fullDir)
{
    String dirNative = getNativePath(fullDir);
    std::pair<dirCacheType::iterator,bool> result = dirCache.insert(dirCacheType::value_type(dirNative, DirListing()));
    if (result.second) {
        DIR *dir = opendir(dirNative.c_str());
        if (!dir)
            {
            error("Could not open directory %s : %s",
                dirNative.c_str(), strerror(errno));
            result.first->second.available = false;
            }
        else
            {
            result.first->second.available = true;
            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 (isDirectory(fullChild))
                    {
                    //trace("directory: %s", childName.c_str());
                    if (!listFiles(baseDir, childName, res))
                        return false;
                    continue;
                    }
                else if (!isRegularFile(fullChild))
                    {
                    error("unknown file:%s", childName.c_str());
                    return false;
                    }

            //all done!
                res.push_back(childName);

                }
            closedir(dir);
            }
    }
    return result.first->second;
}*/

//########################################################################
//# F I L E S E T
//########################################################################
/**
 * This is the descriptor for a <fileset> item
 */
class FileSet
{
public:

    /**
     *
     */
    FileSet()
        {}

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

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

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

    /**
     *
     */
    String getDirectory() const
        { return directory; }
        
    /**
     *
     */
    void setDirectory(const String &val)
        { directory = val; }

    /**
     *
     */
    void setFiles(const std::vector<String> &val)
        { files = val; }

    /**
     *
     */
    std::vector<String> getFiles() const
        { return files; }
        
    /**
     *
     */
    void setIncludes(const std::vector<String> &val)
        { includes = val; }

    /**
     *
     */
    std::vector<String> getIncludes() const
        { return includes; }
        
    /**
     *
     */
    void setExcludes(const std::vector<String> &val)
        { excludes = val; }

    /**
     *
     */
    std::vector<String> getExcludes() const
        { return excludes; }
        
    /**
     *
     */
    std::size_t size() const
        { return files.size(); }
        
    /**
     *
     */
    String operator[](int index) const
        { return files[index]; }
        
    /**
     *
     */
    void clear()
        {
        directory = "";
        files.clear();
        includes.clear();
        excludes.clear();
        }
        

private:

    void assign(const FileSet &other)
        {
        directory = other.directory;
        files     = other.files;
        includes  = other.includes;
        excludes  = other.excludes;
        }

    String directory;
    std::vector<String> files;
    std::vector<String> includes;
    std::vector<String> excludes;
};


//########################################################################
//# F I L E L I S T
//########################################################################
/**
 * This is a simpler, explicitly-named list of files
 */
class FileList
{
public:

    /**
     *
     */
    FileList()
        {}

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

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

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

    /**
     *
     */
    String getDirectory()
        { return directory; }
        
    /**
     *
     */
    void setDirectory(const String &val)
        { directory = val; }

    /**
     *
     */
    void setFiles(const std::vector<String> &val)
        { files = val; }

    /**
     *
     */
    std::vector<String> getFiles()
        { return files; }
        
    /**
     *
     */
    std::size_t size()
        { return files.size(); }
        
    /**
     *
     */
    String operator[](int index)
        { return files[index]; }
        
    /**
     *
     */
    void clear()
        {
        directory = "";
        files.clear();
        }
        

private:

    void assign(const FileList &other)
        {
        directory = other.directory;
        files     = other.files;
        }

    String directory;
    std::vector<String> files;
};




//########################################################################
//# M A K E    B A S E
//########################################################################
/**
 * Base class for all classes in this file
 */
class MakeBase
{
public:

    MakeBase()
        { line = 0; }
    virtual ~MakeBase()
        {}

    /**
     *     Return the URI of the file associated with this object 
     */     
    URI getURI()
        { return uri; }

    /**
     * Set the uri to the given string
     */
    void setURI(const String &uristr)
        { uri.parse(uristr); }

    /**
     * Set the number of threads that can be used
     */
    void setNumThreads(const int num)
        { numThreads = num; }

    /**
     *  Resolve another path relative to this one
     */
    String resolve(const String &otherPath);

    /**
     * replace variable refs like ${a} with their values
     * Assume that the string has already been syntax validated
     */
    String eval(const String &s, const String &defaultVal);

    /**
     * replace variable refs like ${a} with their values
     * return true or false
     * Assume that the string has already been syntax validated
     */
    bool evalBool(const String &s, bool defaultVal);

    /**
     * replace variable refs like ${a} with their values
     * return the value parsed as an integer
     * Assume that the string has already been syntax validated
     */
    int evalInt(const String &s, int defaultVal);

    /**
     *  Get an element attribute, performing substitutions if necessary
     */
    bool getAttribute(Element *elem, const String &name, String &result);

    /**
     * Get an element value, performing substitutions if necessary
     */
    bool getValue(Element *elem, String &result);
    
    /**
     * Set the current line number in the file
     */         
    void setLine(int val)
        { line = val; }
        
    /**
     * Get the current line number in the file
     */         
    int getLine()
        { return line; }


    /**
     * Set a property to a given value
     */
    virtual void setProperty(const String &name, const String &val)
        {
        properties[name] = val;
        }

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

    /**
     * Return true if a named property is found, else false
     */
    virtual bool hasProperty(const String &name)
        {
        std::map<String, String>::iterator iter = properties.find(name);
        if (iter == properties.end())
            return false;
        return true;
        }


protected:

    /**
     *    The path to the file associated with this object
     */     
    URI uri;

    /**
     *    The number of threads that can be used
     */     
    static int numThreads;

    /**
     *    If this prefix is seen in a substitution, use an environment
     *    variable.
     *             example:  <property environment="env"/>
     *             ${env.JAVA_HOME}
     */
    String envPrefix;

    /**
     *    If this prefix is seen in a substitution, use as a
     *    pkg-config 'all' query
     *             example:  <property pkg-config="pc"/>
     *             ${pc.gtkmm}
     */
    String pcPrefix;

    /**
     *    If this prefix is seen in a substitution, use as a
     *    pkg-config 'cflags' query
     *             example:  <property pkg-config="pcc"/>
     *             ${pcc.gtkmm}
     */
    String pccPrefix;

    /**
     *    If this prefix is seen in a substitution, use as a
     *    pkg-config 'libs' query
     *             example:  <property pkg-config-libs="pcl"/>
     *             ${pcl.gtkmm}
     */
    String pclPrefix;

    /**
     *    If this prefix is seen in a substitution, use as a
     *    Bazaar "bzr revno" query
     *             example:  <property subversion="svn"/> ???
     *             ${bzr.Revision}
     */
    String bzrPrefix;





    /**
     *  Print a printf()-like formatted error message
     */
    void error(const char *fmt, ...);

    /**
     *  Print a printf()-like formatted trace message
     */
    void status(const char *fmt, ...);

    /**
     *  Show target status
     */
    void targetstatus(const char *fmt, ...);

    /**
     *  Print a printf()-like formatted trace message
     */
    void trace(const char *fmt, ...);

    /**
     *  Check if a given string matches a given regex pattern
     */
    bool regexMatch(const String &str, const String &pattern);

    /**
     *
     */
    String getSuffix(const String &fname);

    /**
     * Break up a string into substrings delimited the characters
     * in delimiters.  Null-length substrings are ignored
     */  
    std::vector<String> 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 a lower case version of the given string
     */
    String toLower(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<String> &res);

    /**
     * Find all files in the named directory 
     */         
    bool listFiles(const String &baseName,
                   const String &dirname,
                   std::vector<String> &result);

    /**
     * Perform a listing for a fileset 
     */         
    bool listFiles(MakeBase &propRef, FileSet &fileSet);

    /**
     * Parse a <patternset>
     */  
    bool parsePatternSet(Element *elem,
                       MakeBase &propRef,
                       std::vector<String> &includes,
                       std::vector<String> &excludes);

    /**
     * Parse a <fileset> entry, and determine which files
     * should be included
     */  
    bool parseFileSet(Element *elem,
                    MakeBase &propRef,
                    FileSet &fileSet);
    /**
     * Parse a <filelist> entry
     */  
    bool parseFileList(Element *elem,
                    MakeBase &propRef,
                    FileList &fileList);

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


    std::map<String, String> properties;

    /**
     * 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);

    /**
     * Delete a file
     */ 
    bool removeFile(const String &file);

    /**
     * Tests if the file exists
     */ 
    bool fileExists(const String &fileName);

    /**
     * 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:

    bool pkgConfigRecursive(const String packageName,
                            const String &path, 
                            const String &prefix, 
                            int query,
                            String &result,
                            std::set<String> &deplist);

    /**
     * utility method to query for "all", "cflags", or "libs" for this package and its
     * dependencies.  0, 1, 2
     */          
    bool pkgConfigQuery(const String &packageName, int query, String &result);

    /**
     * replace a variable ref like ${a} with a value
     */
    bool lookupProperty(const String &s, String &result);
    
    /**
     * called by getSubstitutions().  This is in case a looked-up string
     * has substitutions also.     
     */
    bool getSubstitutionsRecursive(const String &s, String &result, int depth);

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

    int line;


};

int MakeBase::numThreads = 1;

/**
 * Define the pkg-config class here, since it will be used in MakeBase method
 * implementations. 
 */
class PkgConfig : public MakeBase
{

public:

    /**
     *
     */
    PkgConfig()
        {
         path   = ".";
         prefix = "/target";
         init();
         }

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

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

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

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

    /**
     *
     */
    virtual String getPath()
        { return path; }

    /**
     *
     */
    virtual void setPath(const String &val)
        { path = val; }

    /**
     *
     */
    virtual String getPrefix()
        { return prefix; }

    /**
     *  Allow the user to override the prefix in the file
     */
    virtual void setPrefix(const String &val)
        { prefix = val; }

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

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

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

    /**
     *
     */
    virtual String getAll()
        {
         String ret = cflags;
         ret.append(" ");
         ret.append(libs);
         return ret;
        }

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

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

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

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

    /**
     *
     */
    virtual std::map<String, String> &getAttributes()
        { return attrs; }

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

    /**
     *  Read a file for its details
     */         
    virtual bool readFile(const String &fileName);

    /**
     *  Read a file for its details
     */         
    virtual bool query(const String &name);

private:

    void init()
        {
        //do not set path and prefix here
        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;
        path         = other.path;
        prefix       = other.prefix;
        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);

    /**
     * Very important
     */         
    bool parseRequires();

    void parseVersion();

    bool parseLine(const String &lineBuf);

    bool parse(const String &buf);

    void dumpAttrs();

    String name;

    String path;

    String prefix;

    String description;

    String cflags;

    String libs;

    String requires;

    String version;

    int majorVersion;

    int minorVersion;

    int microVersion;

    String fileName;

    std::map<String, String> attrs;

    std::vector<String> requireList;

    char *parsebuf;
    int parselen;
};

/**
 * Execute the "bzr revno" command and return the result.
 * This is a simple, small class.
 */
class BzrRevno : public MakeBase
{
public:

    /**
     * Safe way. Execute "bzr revno" and return the result.
     * Safe from changes in format.
     */
    bool query(String &res)
    {
        String cmd = "bzr revno";

        String outString, errString;
        bool ret = executeCommand(cmd.c_str(), "", outString, errString);
        if (!ret)
            {
            error("error executing '%s': %s", cmd.c_str(), errString.c_str());
            return false;
            }
        res = outString;
        return true;
    } 
};

/**
 * Execute the "svn info" command and parse the result.
 * This is a simple, small class. Define here, because it
 * is used by MakeBase implementation methods. 
 */
class SvnInfo : public MakeBase
{
public:

#if 0
    /**
     * Safe way. Execute "svn info --xml" and parse the result.  Search for
     * elements/attributes.  Safe from changes in format.
     */
    bool query(const String &name, String &res)
    {
        String cmd = "svn info --xml";
    
        String outString, errString;
        bool ret = executeCommand(cmd.c_str(), "", outString, errString);
        if (!ret)
            {
            error("error executing '%s': %s", cmd.c_str(), errString.c_str());
            return false;
            }
        Parser parser;
        Element *elem = parser.parse(outString); 
        if (!elem)
            {
            error("error parsing 'svn info' xml result: %s", outString.c_str());
            return false;
            }
        
        res = elem->getTagValue(name);
        if (res.size()==0)
            {
            res = elem->getTagAttribute("entry", name);
            }
        return true;
    } 
#else


    /**
     * Universal way.  Parse the file directly.  Not so safe from
     * changes in format.
     */
    bool query(const String &name, String &res)
    {
        String fileName = resolve(".svn/entries");
        String nFileName = getNativePath(fileName);
        
        std::map<String, String> properties;
        
        FILE *f = fopen(nFileName.c_str(), "r");
        if (!f)
            {
            error("could not open SVN 'entries' file");
            return false;
            }

        const char *fieldNames[] =
            {
            "format-nbr",
            "name",
            "kind",
            "revision",
            "url",
            "repos",
            "schedule",
            "text-time",
            "checksum",
            "committed-date",
            "committed-rev",
            "last-author",
            "has-props",
            "has-prop-mods",
            "cachable-props",
            };

        for (int i=0 ; i<15 ; i++)
            {
            inbuf[0] = '\0';
            if (feof(f) || !fgets(inbuf, 255, f))
                break;
            properties[fieldNames[i]] = trim(inbuf);
            }
        fclose(f);
        
        res = properties[name];
        
        return true;
    } 
    
private:

    char inbuf[256];

#endif

};






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



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


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



/**
 *  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;
}



/**
 *  Check if a given string matches a given regex pattern
 */
bool MakeBase::regexMatch(const String &str, const String &pattern)
{
    int res = slre_match(pattern.c_str(), str.c_str(), str.length(), NULL, 0, SLRE_IGNORE_CASE);
    
    bool ret = true;
    if (res < 0)
        {
        ret = false;
        
        // error cases
        if (res < -1)
            {
            String err;
            switch(res)
                {
                    case SLRE_UNEXPECTED_QUANTIFIER:
                        err = "unexpected quantifier"; break;
                    case SLRE_UNBALANCED_BRACKETS:
                        err = "unbalanced brackets"; break;
                    case SLRE_INTERNAL_ERROR:
                        err = "internal error"; break;
                    case SLRE_INVALID_CHARACTER_SET:
                        err = "invald character set"; break;
                    case SLRE_INVALID_METACHARACTER:
                        err = "invalid meta character"; break;
                    default:
                        err = "unknown error"; break;
                }
            error("regex failure (%s) while parsing [%s]!\n", err.c_str(), pattern.c_str());
            }
        }

    return ret;
}

/**
 *  Return the suffix, if any, of a file name
 */
String MakeBase::getSuffix(const String &fname)
{
    if (fname.size() < 2)
        return "";
    std::size_t 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<String> MakeBase::tokenize(const String &str,
                                const String &delimiters)
{

    std::vector<String> res;
    char *del = (char *)delimiters.c_str();
    String dmp;
    for (std::size_t i=0 ; i<str.size() ; i++)
        {
        char ch = str[i];
        char *p = (char *)0;
        for (p=del ; *p ; p++)
            if (*p == ch)
                break;
        if (*p)
            {
            if (dmp.size() > 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<len ; i++)
        {
        char ch = s[i];
        if (isspace(ch))
            {
            stripped.push_back(' ');
            for ( ; i<len ; i++)
                {
                ch = s[i];
                if (!isspace(ch))
                    {
                    stripped.push_back(ch);
                    break;
                    }
                }
            }
        else
            {
            stripped.push_back(ch);
            }
        }
    return stripped;
}

/**
 *  remove leading whitespace from each line
 */
String MakeBase::leftJustify(const String &s)
{
    String out;
    int len = s.size();
    for (int i = 0 ; i<len ; )
        {
        char ch;
        //Skip to first visible character
        while (i<len)
            {
            ch = s[i];
            if (ch == '\n' || ch == '\r'
              || !isspace(ch))
                  break;
            i++;
            }
        //Copy the rest of the line
        while (i<len)
            {
            ch = s[i];
            if (ch == '\n' || ch == '\r')
                {
                if (ch != '\r')
                    out.push_back('\n');
                i++;
                break;
                }
            else
                {
                out.push_back(ch);
                }
            i++;
            }
        }
    return out;
}


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

    //Find first non-ws char, going in reverse
    std::size_t 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;
}


/**
 *  Return a lower case version of the given string
 */
String MakeBase::toLower(const String &s)
{
    if (s.size()==0)
        return s;

    String ret;
    for(std::size_t i=0; i<s.size() ; i++)
        {
        ret.push_back(tolower(s[i]));
        }
    return ret;
}


/**
 * Return the native format of the canonical
 * path which we store
 */
String MakeBase::getNativePath(const String &path)
{
#ifdef __WIN32__
    String npath;
    std::size_t firstChar = 0;
    if (path.size() >= 3)
        {
        if (path[0] == '/' &&
            isalpha(path[1]) &&
            path[2] == ':')
            firstChar++;
        }
    for (std::size_t i=firstChar ; i<path.size() ; i++)
        {
        char ch = path[i];
        if (ch == '/')
            npath.push_back('\\');
        else
            npath.push_back(ch);
        }
    return npath;
#else
    return path;
#endif
}


#ifdef __WIN32__
#include <tchar.h>

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




#ifdef __WIN32__

/**
 * 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());

    outbuf.clear();
    errbuf.clear();
    

    /*
    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());
   
    //# Go to http://msdn2.microsoft.com/en-us/library/ms682499.aspx
    //# to see how Win32 pipes work

    //# 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 (&outbuf != &errbuf) {
        if (!CreatePipe(&stderrRead, &stderrWrite, &saAttr, 0))
            {
            error("executeProgram: could not create pipe");
            delete[] paramBuf;
            return false;
            } 
        SetHandleInformation(stderrRead, HANDLE_FLAG_INHERIT, 0);
    } else {
        stderrRead = stdoutRead;
        stderrWrite = stdoutWrite;
    }

    // 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;
        }

    delete[] paramBuf;

    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 (stdoutWrite != stderrWrite && !CloseHandle(stderrWrite))
        {
        error("executeCommand: could not close read pipe");
        return false;
        }

    bool lastLoop = false;
    while (true)
        {
        DWORD avail;
        DWORD bytesRead;
        char readBuf[4096];

        //trace("## stderr");
        PeekNamedPipe(stderrRead, NULL, 0, NULL, &avail, NULL);
        if (avail > 0)
            {
            bytesRead = 0;
            if (avail>4096) avail = 4096;
            ReadFile(stderrRead, readBuf, avail, &bytesRead, NULL);
            if (bytesRead > 0)
                {
                for (unsigned int i=0 ; i<bytesRead ; i++)
                    errbuf.push_back(readBuf[i]);
                }
            }

        //trace("## stdout");
        PeekNamedPipe(stdoutRead, NULL, 0, NULL, &avail, NULL);
        if (avail > 0)
            {
            bytesRead = 0;
            if (avail>4096) avail = 4096;
            ReadFile(stdoutRead, readBuf, avail, &bytesRead, NULL);
            if (bytesRead > 0)
                {
                for (unsigned int i=0 ; i<bytesRead ; i++)
                    outbuf.push_back(readBuf[i]);
                }
            }
            
        //Was this the final check after program done?
        if (lastLoop)
            break;

        DWORD exitCode;
        GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
        if (exitCode != STILL_ACTIVE)
            lastLoop = true;

        Sleep(10);
        }    
    //trace("outbuf:%s", outbuf.c_str());
    if (!CloseHandle(stdoutRead))
        {
        error("executeCommand: could not close read pipe");
        return false;
        }
    if (stdoutRead != stderrRead && !CloseHandle(stderrRead))
        {
        error("executeCommand: could not close read pipe");
        return false;
        }

    DWORD exitCode;
    GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
    //trace("exit code:%d", exitCode);
    if (exitCode != 0)
        {
        ret = false;
        }
    
    CloseHandle(piProcessInfo.hProcess);
    CloseHandle(piProcessInfo.hThread);

    return ret;

} 

#else  /*do it unix style*/

#include <sys/wait.h>



/**
 * 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());

    outbuf.clear();
    errbuf.clear();
    

    int outfds[2];
    if (pipe(outfds) < 0)
        return false;
    int errfds[2];
    if (pipe(errfds) < 0)
        return false;
    int pid = fork();
    if (pid < 0)
        {
        close(outfds[0]);
        close(outfds[1]);
        close(errfds[0]);
        close(errfds[1]);
        error("launch of command '%s' failed : %s",
             command.c_str(), strerror(errno));
        return false;
        }
    else if (pid > 0) // parent
        {
        close(outfds[1]);
        close(errfds[1]);
        }
    else // == 0, child
        {
        close(outfds[0]);
        dup2(outfds[1], STDOUT_FILENO);
        close(outfds[1]);
        close(errfds[0]);
        dup2(errfds[1], STDERR_FILENO);
        close(errfds[1]);

        char *args[4];
        args[0] = (char *)"sh";
        args[1] = (char *)"-c";
        args[2] = (char *)command.c_str();
        args[3] = NULL;
        execv("/bin/sh", args);
        exit(EXIT_FAILURE);
        }

    String outb;
    String errb;

    int outRead = outfds[0];
    int errRead = errfds[0];
    int max = outRead;
    if (errRead > max)
        max = errRead;

    bool outOpen = true;
    bool errOpen = true;

    while (outOpen || errOpen)
        {
        char ch;
        fd_set fdset;
        FD_ZERO(&fdset);
        if (outOpen)
            FD_SET(outRead, &fdset);
        if (errOpen)
            FD_SET(errRead, &fdset);
        int ret = select(max+1, &fdset, NULL, NULL, NULL);
        if (ret < 0)
            break;
        if (FD_ISSET(outRead, &fdset))
            {
            if (read(outRead, &ch, 1) <= 0)
                { outOpen = false; }
            else if (ch <= 0)
                { /* outOpen = false; */ }
            else
                { outb.push_back(ch); }
            }
        if (FD_ISSET(errRead, &fdset))
            {
            if (read(errRead, &ch, 1) <= 0)
                { errOpen = false; }
            else if (ch <= 0)
                { /* errOpen = false; */ }
            else
                { errb.push_back(ch); }
            }
        }

    int childReturnValue;
    wait(&childReturnValue);

    close(outRead);
    close(errRead);

    outbuf = outb;
    errbuf = errb;

    if (childReturnValue != 0)
        {
        error("exec of command '%s' failed : %s",
             command.c_str(), strerror(childReturnValue));
        return false;
        }

    return true;
} 

#endif




bool MakeBase::listDirectories(const String &baseName,
                              const String &dirName,
                              std::vector<String> &res)
{
    res.push_back(dirName);
    String fullPath = baseName;
    if (dirName.size()>0)
        {
        if (dirName[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 (cachedStat(childNative, &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<String> &res)
{
    String fullDir = baseDir;
    if (dirName.size()>0)
        {
        fullDir.append("/");
        fullDir.append(dirName);
        }
    String dirNative = getNativePath(fullDir);

    std::vector<String> subdirs;
    DIR *dir = opendir(dirNative.c_str());
    if (!dir)
        {
        error("Could not open directory %s : %s",
              dirNative.c_str(), strerror(errno));
        return false;
        }
    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 (isDirectory(fullChild))
            {
            //trace("directory: %s", childName.c_str());
            if (!listFiles(baseDir, childName, res))
                return false;
            continue;
            }
        else if (!isRegularFile(fullChild))
            {
            error("unknown file:%s", childName.c_str());
            return false;
            }

       //all done!
        res.push_back(childName);

        }
    closedir(dir);

    return true;
}


/**
 * Several different classes extend MakeBase.  By "propRef", we mean
 * the one holding the properties.  Likely "Make" itself
 */
bool MakeBase::listFiles(MakeBase &propRef, FileSet &fileSet)
{
    //before doing the list,  resolve any property references
    //that might have been specified in the directory name, such as ${src}
    String fsDir = fileSet.getDirectory();
    String dir;
    if (!propRef.getSubstitutions(fsDir, dir))
        return false;
    String baseDir = propRef.resolve(dir);
    std::vector<String> fileList;
    if (!listFiles(baseDir, "", fileList))
        return false;

    std::vector<String> includes = fileSet.getIncludes();
    std::vector<String> excludes = fileSet.getExcludes();

    std::vector<String> incs;
    std::vector<String>::iterator iter;

    std::sort(fileList.begin(), fileList.end());

    //If there are <includes>, then add files to the output
    //in the order of the include list
    if (includes.size()==0)
        incs = fileList;
    else
        {
        for (iter = includes.begin() ; iter != includes.end() ; iter++)
            {
            String &pattern = *iter;
            std::vector<String>::iterator siter;
            for (siter = fileList.begin() ; siter != fileList.end() ; siter++)
                {
                String s = *siter;
                if (regexMatch(s, pattern))
                    {
                    //trace("INCLUDED:%s", s.c_str());
                    incs.push_back(s);
                    }
                }
            }
        }

    //Now trim off the <excludes>
    std::vector<String> res;
    for (iter = incs.begin() ; iter != incs.end() ; iter++)
        {
        String s = *iter;
        bool skipme = false;
        std::vector<String>::iterator siter;
        for (siter = excludes.begin() ; siter != excludes.end() ; siter++)
            {
            String &pattern = *siter;
            if (regexMatch(s, pattern))
                {
                //trace("EXCLUDED:%s", s.c_str());
                skipme = true;
                break;
                }
            }
        if (!skipme)
            res.push_back(s);
        }
        
    fileSet.setFiles(res);

    return true;
}


/**
 * 0 == all, 1 = cflags, 2 = libs
 */ 
bool MakeBase::pkgConfigRecursive(const String packageName,
                                  const String &path, 
                                  const String &prefix, 
                                  int query,
                                  String &result,
                                  std::set<String> &deplist) 
{
    PkgConfig pkgConfig;
    if (path.size() > 0)
        pkgConfig.setPath(path);
    if (prefix.size() > 0)
        pkgConfig.setPrefix(prefix);
    if (!pkgConfig.query(packageName))
        return false;
    if (query == 0)
        result = pkgConfig.getAll();
    else if (query == 1)
        result = pkgConfig.getCflags();
    else
        result = pkgConfig.getLibs();
    deplist.insert(packageName);
    std::vector<String> list = pkgConfig.getRequireList();
    for (std::size_t i = 0 ; i<list.size() ; i++)
        {
        String depPkgName = list[i];
        if (deplist.find(depPkgName) != deplist.end())
            continue;
        String val;
        if (!pkgConfigRecursive(depPkgName, path, prefix, query, val, deplist))
            {
            error("Based on 'requires' attribute of package '%s'", packageName.c_str());
            return false;
            }
        result.append(" ");
        result.append(val);
        }

    return true;
}

bool MakeBase::pkgConfigQuery(const String &packageName, int query, String &result)
{
    std::set<String> deplist;
    String path = getProperty("pkg-config-path");
    if (path.size()>0)
        path = resolve(path);
    String prefix = getProperty("pkg-config-prefix");
    String val;
    if (!pkgConfigRecursive(packageName, path, prefix, query, val, deplist))
        return false;
    result = val;
    return true;
}



/**
 * replace a variable ref like ${a} with a value
 */
bool MakeBase::lookupProperty(const String &propertyName, String &result)
{
    String varname = propertyName;
    if (envPrefix.size() > 0 &&
        varname.compare(0, envPrefix.size(), envPrefix) == 0)
        {
        varname = varname.substr(envPrefix.size());
        char *envstr = getenv(varname.c_str());
        if (!envstr)
            {
            error("environment variable '%s' not defined", varname.c_str());
            return false;
            }
        result = envstr;
        }
    else if (pcPrefix.size() > 0 &&
        varname.compare(0, pcPrefix.size(), pcPrefix) == 0)
        {
        varname = varname.substr(pcPrefix.size());
        String val;
        if (!pkgConfigQuery(varname, 0, val))
            return false;
        result = val;
        }
    else if (pccPrefix.size() > 0 &&
        varname.compare(0, pccPrefix.size(), pccPrefix) == 0)
        {
        varname = varname.substr(pccPrefix.size());
        String val;
        if (!pkgConfigQuery(varname, 1, val))
            return false;
        result = val;
        }
    else if (pclPrefix.size() > 0 &&
        varname.compare(0, pclPrefix.size(), pclPrefix) == 0)
        {
        varname = varname.substr(pclPrefix.size());
        String val;
        if (!pkgConfigQuery(varname, 2, val))
            return false;
        result = val;
        }
    else if (bzrPrefix.size() > 0 &&
        varname.compare(0, bzrPrefix.size(), bzrPrefix) == 0)
        {
        varname = varname.substr(bzrPrefix.size());
        String val;
        //SvnInfo svnInfo;
        BzrRevno bzrRevno;
        if (varname == "revision")
	    {
            if (!bzrRevno.query(val))
                return "";
            result = "r"+val;
        }
        /*if (!svnInfo.query(varname, val))
            return false;
        result = val;*/
        }
    else
        {
        std::map<String, String>::iterator iter;
        iter = properties.find(varname);
        if (iter != properties.end())
            {
            result = iter->second;
            }
        else
            {
            error("property '%s' not found", varname.c_str());
            return false;
            }
        }
    return true;
}




/**
 * Analyse a string, looking for any substitutions or other
 * things that need resolution 
 */
bool MakeBase::getSubstitutionsRecursive(const String &str,
                                         String &result, int depth)
{
    if (depth > 10)
        {
        error("nesting of substitutions too deep (>10) for '%s'",
                        str.c_str());
        return false;
        }
    String s = trim(str);
    int len = (int)s.size();
    String val;
    for (int i=0 ; i<len ; i++)
        {
        char ch = s[i];
        if (ch == '$' && s[i+1] == '{')
            {
            String varname;
            int j = i+2;
            for ( ; j<len ; j++)
                {
                ch = s[j];
                if (ch == '$' && s[j+1] == '{')
                    {
                    error("attribute %s cannot have nested variable references",
                           s.c_str());
                    return false;
                    }
                else if (ch == '}')
                    {
                    varname = trim(varname);
                    String varval;
                    if (!lookupProperty(varname, varval))
                        return false;
                    String varval2;
                    //Now see if the answer has ${} in it, too
                    if (!getSubstitutionsRecursive(varval, varval2, depth + 1))
                        return false;
                    val.append(varval2);
                    break;
                    }
                else
                    {
                    varname.push_back(ch);
                    }
                }
            i = j;
            }
        else
            {
            val.push_back(ch);
            }
        }
    result = val;
    return true;
}

/**
 * Analyse a string, looking for any substitutions or other
 * things that need resilution 
 */
bool MakeBase::getSubstitutions(const String &str, String &result)
{
    return getSubstitutionsRecursive(str, result, 0);
}



/**
 * replace variable refs like ${a} with their values
 * Assume that the string has already been syntax validated
 */
String MakeBase::eval(const String &s, const String &defaultVal)
{
    if (s.size()==0)
        return defaultVal;
    String ret;
    if (getSubstitutions(s, ret))
        return ret;
    else
        return defaultVal;
}


/**
 * replace variable refs like ${a} with their values
 * return true or false
 * Assume that the string has already been syntax validated
 */
bool MakeBase::evalBool(const String &s, bool defaultVal)
{
    if (s.size()==0)
        return defaultVal;
    String val = eval(s, "false");
    if (val.size()==0)
        return defaultVal;
    if (val == "true" || val == "TRUE")
        return true;
    else
        return false;
}

int MakeBase::evalInt(const String &s, int defaultVal)
{
    if (s.size()==0) {
        return defaultVal;
    }
    int val = atoi(s.c_str());
    // perhaps some error checking, but... bah! waste of time
    return val;
}

/**
 * Get a string attribute, testing it for proper syntax and
 * property names.
 */
bool MakeBase::getAttribute(Element *elem, const String &name,
                                    String &result)
{
    String s = elem->getAttribute(name);
    String tmp;
    bool ret = getSubstitutions(s, tmp);
    if (ret)
        result = s;  //assign -if- ok
    return ret;
}


/**
 * Get a string value, testing it for proper syntax and
 * property names.
 */
bool MakeBase::getValue(Element *elem, String &result)
{
    String s = elem->getValue();
    String tmp;
    bool ret = getSubstitutions(s, tmp);
    if (ret)
        result = s;  //assign -if- ok
    return ret;
}




/**
 * Parse a <patternset> entry
 */  
bool MakeBase::parsePatternSet(Element *elem,
                          MakeBase &propRef,
                          std::vector<String> &includes,
                          std::vector<String> &excludes
                          )
{
    std::vector<Element *> children  = elem->getChildren();
    for (std::size_t i=0 ; i<children.size() ; i++)
        {
        Element *child = children[i];
        String tagName = child->getName();
        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 <fileset> entry, and determine which files
 * should be included
 */  
bool MakeBase::parseFileSet(Element *elem,
                          MakeBase &propRef,
                          FileSet &fileSet)
{
    String name = elem->getName();
    if (name != "fileset")
        {
        error("expected <fileset>");
        return false;
        }


    std::vector<String> includes;
    std::vector<String> excludes;

    //A fileset has one implied patternset
    if (!parsePatternSet(elem, propRef, includes, excludes))
        {
        return false;
        }
    //Look for child tags, including more patternsets
    std::vector<Element *> children  = elem->getChildren();
    for (std::size_t i=0 ; i<children.size() ; i++)
        {
        Element *child = children[i];
        String tagName = child->getName();
        if (tagName == "patternset")
            {
            if (!parsePatternSet(child, propRef, includes, excludes))
                {
                return false;
                }
            }
        }

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

    fileSet.setDirectory(dir);
    fileSet.setIncludes(includes);
    fileSet.setExcludes(excludes);
    
    /*
    std::vector<String> fileList;
    if (dir.size() > 0)
        {
        String baseDir = propRef.resolve(dir);
        if (!listFiles(baseDir, "", includes, excludes, fileList))
            return false;
        }
    std::sort(fileList.begin(), fileList.end());
    result = fileList;
    */

    
    /*
    for (std::size_t i=0 ; i<result.size() ; i++)
        {
        trace("RES:%s", result[i].c_str());
        }
    */

    
    return true;
}

/**
 * Parse a <filelist> entry.  This is far simpler than FileSet,
 * since no directory scanning is needed.  The file names are listed
 * explicitly.
 */  
bool MakeBase::parseFileList(Element *elem,
                          MakeBase &propRef,
                          FileList &fileList)
{
    std::vector<String> fnames;
    //Look for child tags, namely "file"
    std::vector<Element *> children  = elem->getChildren();
    for (std::size_t i=0 ; i<children.size() ; i++)
        {
        Element *child = children[i];
        String tagName = child->getName();
        if (tagName == "file")
            {
            String fname = child->getAttribute("name");
            if (fname.size()==0)
                {
                error("<file> element requires name="" attribute");
                return false;
                }
            fnames.push_back(fname);
            }
        else
            {
            error("tag <%s> not allowed in <fileset>", tagName.c_str());
            return false;
            }
        }

    String dir;
    //Get the base directory for reading file names
    if (!propRef.getAttribute(elem, "dir", dir))
        return false;
    fileList.setDirectory(dir);
    fileList.setFiles(fnames);

    return true;
}



/**
 * Create a directory, making intermediate dirs
 * if necessary
 */                  
bool MakeBase::createDirectory(const String &dirname)
{
    //trace("## createDirectory: %s", dirname.c_str());
    //## first check if it exists
    struct stat finfo;
    String nativeDir = getNativePath(dirname);
    char *cnative = (char *) nativeDir.c_str();
#ifdef __WIN32__
    if (strlen(cnative)==2 && cnative[1]==':')
        return true;
#endif
    if (cachedStat(nativeDir, &finfo)==0)
        {
        if (!S_ISDIR(finfo.st_mode))
            {
            error("mkdir: file %s exists but is not a directory",
                  cnative);
            return false;
            }
        else //exists
            {
            return true;
            }
        }

    //## 2: pull off the last path segment, if any,
    //## to make the dir 'above' this one, if necessary
    std::size_t pos = dirname.find_last_of('/');
    if (pos>0 && pos != dirname.npos)
        {
        String subpath = dirname.substr(0, pos);
        //A letter root (c:) ?
        if (!createDirectory(subpath))
            return false;
        }
        
    //## 3: now make
#ifdef __WIN32__
    if (mkdir(cnative)<0)
#else
    if (mkdir(cnative, S_IRWXU | S_IRWXG | S_IRWXO)<0)
#endif
        {
        error("cannot make directory '%s' : %s",
                 cnative, strerror(errno));
        return false;
        }

    removeFromStatCache(nativeDir);
        
    return true;
}


/**
 * Remove a directory recursively
 */ 
bool MakeBase::removeDirectory(const String &dirName)
{
    char *dname = (char *)dirName.c_str();

    DIR *dir = opendir(dname);
    if (!dir)
        {
        //# Let this fail nicely.
        return true;
        //error("error opening directory %s : %s", dname, strerror(errno));
        //return false;
        }
    
    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);


        struct stat finfo;
        String childNative = getNativePath(childName);
        char *cnative = (char *)childNative.c_str();
        if (cachedStat(childNative, &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 (!removeFile(childName))
                {
                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;
        }

    removeFromStatCache(native);

    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 (cachedStat(srcNative, &srcinfo)<0)
        {
        error("source file %s for copy does not exist",
                 srcNative.c_str());
        return false;
        }

    String destNative = getNativePath(destFile);
    struct stat destinfo;
    if (cachedStat(destNative, &destinfo)==0)
        {
        if (destinfo.st_mtime >= srcinfo.st_mtime)
            return true;
        }
        
    //# 2 prepare a destination directory if necessary
    std::size_t 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)
        {
        fclose(srcf);
        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__ */

    removeFromStatCache(destNative);

    return true;
}


/**
 * Delete a file
 */ 
bool MakeBase::removeFile(const String &file)
{
    String native = getNativePath(file);

    if (!fileExists(native))
        {
        return true;
        }

#ifdef WIN32
    // On Windows 'remove' will only delete files

    if (remove(native.c_str())<0)
        {
        if (errno==EACCES)
            {
            error("File %s is read-only", native.c_str());
            }
        else if (errno==ENOENT)
            {
            error("File %s does not exist or is a directory", native.c_str());
            }
        else
            {
            error("Failed to delete file %s: %s", native.c_str(), strerror(errno));
            }
        return false;
        }

#else

    if (!isRegularFile(native))
        {
        error("File %s does not exist or is not a regular file", native.c_str());
        return false;
        }

    if (remove(native.c_str())<0)
        {
        if (errno==EACCES)
            {
            error("File %s is read-only", native.c_str());
            }
        else
            {
            error(
                errno==EACCES ? "File %s is read-only" :
                errno==ENOENT ? "File %s does not exist or is a directory" :
                "Failed to delete file %s: %s", native.c_str());
            }
        return false;
        }

#endif

    removeFromStatCache(native);

    return true;
}


/**
 * Tests if the file exists
 */ 
bool MakeBase::fileExists(const String &fileName)
{
    String native = getNativePath(fileName);
    struct stat finfo;
    
    //Exists?
    if (cachedStat(native, &finfo)<0)
        return false;

    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 (cachedStat(native, &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 (cachedStat(native, &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 (cachedStat(nativeA, &infoA)<0)
        {
        return false;
        }

    String nativeB = getNativePath(fileB);
    struct stat infoB;
    //IF dest does not exist, YES, newer
    if (cachedStat(nativeB, &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
//########################################################################


/**
 * 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.
 *  Pkg-config is line-oriented, so check for newline
 */
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 != '+' && ch != '.')
            break;
        ret.push_back((char)ch);
        pos++;
        }
    return pos;
}

bool PkgConfig::parseRequires()
{
    if (requires.size() == 0)
        return true;
    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);
        }
    return true;
}


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;
    std::size_t pos = 0;
    std::size_t 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::parseLine(const String &lineBuf)
{
    parsebuf = (char *)lineBuf.c_str();
    parselen = lineBuf.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 %s", subName.c_str(), prefix.c_str());
                if (subName == "prefix" && prefix.size()>0)
                    {
                    attrVal.append(prefix);
                    //trace("prefix override:%s", prefix.c_str());
                    }
                else
                    {
                    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;

        String attrNameL = toLower(attrName);

        if (attrNameL == "name")
            name = attrVal;
        else if (attrNameL == "description")
            description = attrVal;
        else if (attrNameL == "cflags")
            cflags = attrVal;
        else if (attrNameL == "libs")
            libs = attrVal;
        else if (attrNameL == "requires")
            requires = attrVal;
        else if (attrNameL == "version")
            version = attrVal;

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

    return true;
}


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

    String line;
    int lineNr = 0;
    for (std::size_t p=0 ; p<buf.size() ; p++)
        {
        int ch = buf[p];
        if (ch == '\n' || ch == '\r')
            {
            if (!parseLine(line))
                return false;
            line.clear();
            lineNr++;
            }
        else
            {
            line.push_back(ch);
            }
        }
    if (line.size()>0)
        {
        if (!parseLine(line))
            return false;
        }

    parseRequires();
    parseVersion();

    return true;
}




void PkgConfig::dumpAttrs()
{
    //trace("### PkgConfig attributes for %s", fileName.c_str());
    std::map<String, String>::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 &fname)
{
    fileName = getNativePath(fname);

    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;
}



bool PkgConfig::query(const String &pkgName)
{
    name = pkgName;

    String fname = path;
    fname.append("/");
    fname.append(name);
    fname.append(".pc");

    if (!readFile(fname))
        {
        error("Cannot find package '%s'. Do you have it installed?",
                       pkgName.c_str());
        return false;
        }
    
    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<String, FileRec *> 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<String> files;


private:

    void init()
        {
        }

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

};


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<String> &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<DepRec> loadDepFile(const String &fileName);

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

    /**
     *  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, const char *key);

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

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

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

    /**
     *
     */
    String sourceDir;

    /**
     *
     */
    std::vector<String> fileList;

    /**
     *
     */
    std::vector<String> directories;

    /**
     * A list of all files which will be processed for
     * dependencies.
     */
    std::map<String, FileRec *> allFiles;

    /**
     * The list of .o files, and the
     * dependencies upon them.
     */
    std::map<String, FileRec *> oFiles;

    int depFileSize;
    char *depFileBuf;

    static const int readBufSize = 8192;
    char readBuf[8193];//byte larger

};





/**
 *  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 output file list
    std::map<String, FileRec *>::iterator iter;
    for (iter=oFiles.begin(); iter!=oFiles.end() ; iter++)
        delete iter->second;
    oFiles.clear();

    //allFiles actually contains the master copies. delete them
    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;

    std::size_t pos = fullname.find_last_of('/');
    if (pos != fullname.npos && pos<fullname.size()-1)
        {
        path = fullname.substr(0, pos);
        pos++;
        basename = fullname.substr(pos, fullname.size()-pos);
        }
    else
        {
        path = "";
        basename = fullname;
        }

    pos = basename.find_last_of('.');
    if (pos != basename.npos && pos<basename.size()-1)
        {
        suffix   = basename.substr(pos+1, basename.size()-pos-1);
        basename = basename.substr(0, pos);
        }

    //trace("parsename:%s %s %s", path.c_str(),
    //        basename.c_str(), suffix.c_str()); 
}



/**
 *  Generate our internal file list.
 */
bool DepTool::createFileList()
{

    for (std::size_t i=0 ; i<fileList.size() ; i++)
        {
        String fileName = fileList[i];
        //trace("## FileName:%s", fileName.c_str());
        String path;
        String basename;
        String sfx;
        parseName(fileName, path, basename, sfx);
        if (sfx == "cpp" || sfx == "c" || sfx == "cxx"   ||
            sfx == "cc" || sfx == "CC")
            {
            FileRec *fe         = new FileRec(FileRec::CFILE);
            fe->path            = 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, const 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)
{
    //# if the name is an exact match to a path name
    //# in allFiles, like "myinc.h"
    std::map<String, FileRec *>::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 
        {
        //## Ok, it was not found directly
        //look in other dirs
        std::vector<String>::iterator diter;
        for (diter=directories.begin() ;
             diter!=directories.end() ; diter++)
            {
            String dfname = *diter;
            dfname.append("/");
            dfname.append(iname);
            URI fullPathURI(dfname);  //normalize path name
            String fullPath = fullPathURI.getPath();
            if (fullPath[0] == '/')
                fullPath = fullPath.substr(1);
            //trace("Normalized %s to %s", dfname.c_str(), fullPath.c_str());
            iter = allFiles.find(fullPath);
            if (iter != allFiles.end())
                {
                FileRec *other = iter->second;
                //trace("other: '%s'", iname.c_str());
                frec->files[fullPath] = 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 (!feof(f))
        {
        int nrbytes = fread(readBuf, 1, readBufSize, f);
        readBuf[nrbytes] = '\0';
        buf.append(readBuf);
        }
    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)
{
    std::map<String, FileRec *>::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);
        }


    return true;
}





/**
 *  Generate the file dependency list.
 */
bool DepTool::generateDependencies()
{
    std::map<String, FileRec *>::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");
            oFiles[fname]    = ofile;
            //add the .c file first?   no, don't
            //ofile->files[cFileName] = include;
            
            //trace("ofile:%s", fname.c_str());

            processDependency(ofile, include);
            }
        }

      
    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, "<?xml version='1.0'?>\n");
    fprintf(f, "<!--\n");
    fprintf(f, "########################################################\n");
    fprintf(f, "## File: build.dep\n");
    fprintf(f, "## Generated by BuildTool at :%s", ctime(&tim));
    fprintf(f, "########################################################\n");
    fprintf(f, "-->\n");

    fprintf(f, "<dependencies source='%s'>\n\n", sourceDir.c_str());
    std::map<String, FileRec *>::iterator iter;
    for (iter=oFiles.begin() ; iter!=oFiles.end() ; iter++)
        {
        FileRec *frec = iter->second;
        if (frec->type == FileRec::OFILE)
            {
            fprintf(f, "<object path='%s' name='%s' suffix='%s'>\n",
                 frec->path.c_str(), frec->baseName.c_str(), frec->suffix.c_str());
            std::map<String, FileRec *>::iterator citer;
            for (citer=frec->files.begin() ; citer!=frec->files.end() ; citer++)
                {
                String cfname = citer->first;
                fprintf(f, "    <dep name='%s'/>\n", cfname.c_str());
                }
            fprintf(f, "</object>\n\n");
            }
        }

    fprintf(f, "</dependencies>\n");
    fprintf(f, "\n");
    fprintf(f, "<!--\n");
    fprintf(f, "########################################################\n");
    fprintf(f, "## E N D\n");
    fprintf(f, "########################################################\n");
    fprintf(f, "-->\n");

    fclose(f);

    return true;
}




/**
 *   This loads the dependency cache.
 */
std::vector<DepRec> DepTool::loadDepFile(const String &depFile)
{
    std::vector<DepRec> 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("loadDepFile: main xml element should be <dependencies>");
        delete root;
        return result;
        }

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

    std::vector<Element *> objects = depList->getChildren();
    for (std::size_t i=0 ; i<objects.size() ; i++)
        {
        Element *objectElem = objects[i];
        String tagName = objectElem->getName();
        if (tagName != "object")
            {
            error("loadDepFile: <dependencies> should have only <object> children");
            return result;
            }

        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<Element *> depElems = objectElem->getChildren();
        for (std::size_t i=0 ; i<depElems.size() ; i++)
            {
            Element *depElem = depElems[i];
            tagName = depElem->getName();
            if (tagName != "dep")
                {
                error("loadDepFile: <object> should have only <dep> children");
                return result;
                }
            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<DepRec>::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<DepRec> DepTool::getDepFile(const String &depFile,
                   bool forceRefresh)
{
    std::vector<DepRec> result;
    if (forceRefresh)
        {
        generateDependencies(depFile);
        result = loadDepFile(depFile);
        }
    else
        {
        //try once
        result = loadDepFile(depFile);
        if (result.size() == 0)
            {
            //fail? try again
            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_CC,
        TASK_COPY,
        TASK_CXXTEST_PART,
        TASK_CXXTEST_ROOT,
        TASK_CXXTEST_RUN,
        TASK_DELETE,
        TASK_ECHO,
        TASK_JAR,
        TASK_JAVAC,
        TASK_LINK,
        TASK_MAKEFILE,
        TASK_MKDIR,
        TASK_MSGFMT,
        TASK_PKG_CONFIG,
        TASK_RANLIB,
        TASK_RC,
        TASK_SHAREDLIB,
        TASK_STATICLIB,
        TASK_STRIP,
        TASK_TOUCH,
        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, int lineNr);


protected:

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

    void assign(const Task &other)
        {
        type = other.type;
        name = other.name;
        }
        
    /**
     *  Show task status
     */
    void taskstatus(const char *fmt, ...)
        {
        va_list args;
        va_start(args,fmt);
        fprintf(stdout, "    %s : ", name.c_str());
        vfprintf(stdout, fmt, args);
        fprintf(stdout, "\n");
        va_end(args) ;
        }

    String getAttribute(Element *elem, const String &attrName)
        {
        String str;
        return str;
        }

    MakeBase &parent;

    int type;

    String name;
};



/**
 * 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";
        }

    virtual ~TaskCC()
        {}
        
    virtual bool isExcludedInc(const String &dirname)
        {
        for (std::size_t i=0 ; i<excludeInc.size() ; i++)
            {
            String fname = excludeInc[i];
            if (fname == dirname)
                return true;
            }
        return false;
        }

    virtual bool execute()
        {
        //evaluate our parameters
        String command         = parent.eval(commandOpt, "gcc");
        String ccCommand       = parent.eval(ccCommandOpt, "gcc");
        String cxxCommand      = parent.eval(cxxCommandOpt, "g++");
        String source          = parent.eval(sourceOpt, ".");
        String dest            = parent.eval(destOpt, ".");
        String ccflags         = parent.eval(flagsOpt, "");
        String cxxflags        = parent.eval(cxxflagsOpt, "");
        String defines         = parent.eval(definesOpt, "");
        String includes        = parent.eval(includesOpt, "");
        bool continueOnError   = parent.evalBool(continueOnErrorOpt, true);
        bool refreshCache      = parent.evalBool(refreshCacheOpt, false);

        if (!listFiles(parent, fileSet))
            return false;
            
        FILE *f = NULL;
        f = fopen("compile.lst", "w");

        //refreshCache is probably false here, unless specified otherwise
        String fullName = parent.resolve("build.dep");
        if (refreshCache || isNewerThan(parent.getURI().getPath(), fullName))
            {
            taskstatus("regenerating C/C++ dependency cache");
            refreshCache = true;
            }

        DepTool depTool;
        depTool.setSourceDirectory(source);
        depTool.setFileList(fileSet.getFiles());
        std::vector<DepRec> deps =
             depTool.getDepFile("build.dep", refreshCache);
        
        String incs;
        incs.append("-I");
        incs.append(parent.resolve("."));
        incs.append(" ");
        if (includes.size()>0)
            {
            incs.append(includes);
            incs.append(" ");
            }
        std::set<String> paths;
        std::vector<DepRec>::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<String>::iterator setIter;
        for (setIter=paths.begin() ; setIter!=paths.end() ; setIter++)
            {
            String dirName = *setIter;
            //check excludeInc to see if we dont want to include this dir
            if (isExcludedInc(dirName))
                continue;
            incs.append(" -I");
            String dname;
            if (source.size()>0)
                {
                dname.append(source);
                dname.append("/");
                }
            dname.append(dirName);
            incs.append(parent.resolve(dname));
            }

// First create all directories, fails if done in OpenMP parallel loop below... goes superfast anyway, so don't optimize
        for (std::size_t fi = 0; fi < deps.size() ; ++fi)
        {
            DepRec dep = deps[fi];
 
            //## Make paths
            String destPath = dest;
            if (dep.path.size()>0)
            {
                destPath.append("/");
                destPath.append(dep.path);
            }
            //## Make sure destination directory exists
            if (!createDirectory(destPath))
            {
                taskstatus("problem creating folder: %s", destPath.c_str());
                if (f) {
                    fclose(f);
                }
                return false;
            }
        }

        /**
         * Compile each of the C files that need it
         */
        bool errorOccurred = false;

#ifdef _OPENMP 
        taskstatus("compile with %d threads in parallel", numThreads);
#       pragma omp parallel for num_threads(numThreads)
#endif

        for (std::size_t fi = 0; fi < deps.size() ; ++fi)
        {
            DepRec dep = deps[fi];

            //## Select command
            String sfx = dep.suffix;
            String command = ccCommand;
            String flags = ccflags;
            if (sfx == "cpp" || sfx == "cxx" || sfx == "c++" ||
                 sfx == "cc" || sfx == "CC")
            {
                command = cxxCommand;
                flags += " " + cxxflags;
            }
 
            //## 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);
            }

            //## Check whether it needs to be done
            String destName;
            if (destPath.size()>0)
                {
                destName.append(destPath);
                destName.append("/");
                }
            destName.append(dep.name);
            destName.append(".o");
            String destFullName = parent.resolve(destName);
            String srcName;
            if (srcPath.size()>0)
                {
                srcName.append(srcPath);
                srcName.append("/");
                }
            srcName.append(dep.name);
            srcName.append(".");
            srcName.append(dep.suffix);
            String srcFullName = parent.resolve(srcName);
            bool compileMe = false;
            //# First we check if the source is newer than the .o
            if (isNewerThan(srcFullName, destFullName))
                {
//                taskstatus("compile of %s (req. by: %s)",
//                        destFullName.c_str(), srcFullName.c_str());
                fprintf(stdout, "compile %s\n", srcFullName.c_str());
                compileMe = true;
                }
            else
                {
                //# secondly, we check if any of the included dependencies
                //# of the .c/.cpp is newer than the .o
                for (std::size_t i=0 ; i<dep.files.size() ; i++)
                    {
                    String depName;
                    if (source.size()>0)
                        {
                        depName.append(source);
                        depName.append("/");
                        }
                    depName.append(dep.files[i]);
                    String depFullName = parent.resolve(depName);
                    bool depRequires = isNewerThan(depFullName, destFullName);
                    //trace("%d %s %s\n", depRequires,
                    //        destFullName.c_str(), depFullName.c_str());
                    if (depRequires)
                        {
                        taskstatus("compile %s (%s modified)",
                                srcFullName.c_str(), depFullName.c_str());
                        compileMe = true;
                        break;
                        }
                    }
                }
            if (!compileMe)
                {
                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;
            bool ret = executeCommand(cmd.c_str(), "", outString, errString);

            if (f)
                {
                fprintf(f, "########################### File : %s\n",
                             srcFullName.c_str());
                fprintf(f, "#### COMMAND ###\n");
                int col = 0;
                for (std::size_t i = 0 ; i < cmd.size() ; i++)
                    {
                    char ch = cmd[i];
                    if (isspace(ch)  && col > 63)
                        {
                        fputc('\n', f);
                        col = 0;
                        }
                    else
                        {
                        fputc(ch, f);
                        col++;
                        }
                    if (col > 76)
                        {
                        fputc('\n', f);
                        col = 0;
                        }
                    }
                fprintf(f, "\n");
                fprintf(f, "#### STDOUT ###\n%s\n", outString.c_str());
                fprintf(f, "#### STDERR ###\n%s\n\n", errString.c_str());
                fflush(f);
                }
            if (!ret) {
                error("problem compiling: %s", errString.c_str());
                errorOccurred = true;
            } else if (!errString.empty()) {
                fprintf(stdout, "STDERR: \n%s\n", errString.c_str());
            }


            if (errorOccurred && !continueOnError) {
#ifndef _OPENMP // figure out a way to break the loop here with OpenMP
                break;
#endif
            }

            removeFromStatCache(getNativePath(destFullName));
        }

        if (f)
            {
            fclose(f);
            }
        
        return !errorOccurred;
        }


    virtual bool parse(Element *elem)
        {
        String s;
        if (!parent.getAttribute(elem, "command", commandOpt))
            return false;
        if (commandOpt.size()>0)
            { cxxCommandOpt = ccCommandOpt = commandOpt; }
        if (!parent.getAttribute(elem, "cc", ccCommandOpt))
            return false;
        if (!parent.getAttribute(elem, "cxx", cxxCommandOpt))
            return false;
        if (!parent.getAttribute(elem, "destdir", destOpt))
            return false;
        if (!parent.getAttribute(elem, "continueOnError", continueOnErrorOpt))
            return false;
        if (!parent.getAttribute(elem, "refreshCache", refreshCacheOpt))
            return false;

        std::vector<Element *> children = elem->getChildren();
        for (std::size_t i=0 ; i<children.size() ; i++)
            {
            Element *child = children[i];
            String tagName = child->getName();
            if (tagName == "flags")
                {
                if (!parent.getValue(child, flagsOpt))
                    return false;
                flagsOpt = strip(flagsOpt);
                }
            else if (tagName == "cxxflags")
                {
                if (!parent.getValue(child, cxxflagsOpt))
                    return false;
                cxxflagsOpt = strip(cxxflagsOpt);
                }
            else if (tagName == "includes")
                {
                if (!parent.getValue(child, includesOpt))
                    return false;
                includesOpt = strip(includesOpt);
                }
            else if (tagName == "defines")
                {
                if (!parent.getValue(child, definesOpt))
                    return false;
                definesOpt = strip(definesOpt);
                }
            else if (tagName == "fileset")
                {
                if (!parseFileSet(child, parent, fileSet))
                    return false;
                sourceOpt = fileSet.getDirectory();
                }
            else if (tagName == "excludeinc")
                {
                if (!parseFileList(child, parent, excludeInc))
                    return false;
                }
            }

        return true;
        }
        
protected:

    String   commandOpt;
    String   ccCommandOpt;
    String   cxxCommandOpt;
    String   sourceOpt;
    String   destOpt;
    String   flagsOpt;
    String   cxxflagsOpt;
    String   definesOpt;
    String   includesOpt;
    String   continueOnErrorOpt;
    String   refreshCacheOpt;
    FileSet  fileSet;
    FileList excludeInc;
    
};



/**
 *
 */
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;
        haveFileSet = false;
        }

    virtual ~TaskCopy()
        {}

    virtual bool execute()
        {
        String fileName   = parent.eval(fileNameOpt   , ".");
        String toFileName = parent.eval(toFileNameOpt , ".");
        String toDirName  = parent.eval(toDirNameOpt  , ".");
        bool   verbose    = parent.evalBool(verboseOpt, false);
        switch (cptype)
           {
           case CP_TOFILE:
               {
               if (fileName.size()>0)
                   {
                   taskstatus("%s to %s",
                        fileName.c_str(), toFileName.c_str());
                   String fullSource = parent.resolve(fileName);
                   String fullDest = parent.resolve(toFileName);
                   if (verbose)
                       taskstatus("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))
                       {
                       taskstatus("skipped");
                       return true;
                       }
                   if (!copyFile(fullSource, fullDest))
                       return false;
                   taskstatus("1 file copied");
                   }
               return true;
               }
           case CP_TODIR:
               {
               if (haveFileSet)
                   {
                   if (!listFiles(parent, fileSet))
                       return false;
                   String fileSetDir = parent.eval(fileSet.getDirectory(), ".");

                   taskstatus("%s to %s",
                       fileSetDir.c_str(), toDirName.c_str());

                   int nrFiles = 0;
                   for (std::size_t i=0 ; i<fileSet.size() ; i++)
                       {
                       String fileName = fileSet[i];

                       String sourcePath;
                       if (fileSetDir.size()>0)
                           {
                           sourcePath.append(fileSetDir);
                           sourcePath.append("/");
                           }
                       sourcePath.append(fileName);
                       String fullSource = parent.resolve(sourcePath);
                       
                       //Get the immediate parent directory's base name
                       String baseFileSetDir = fileSetDir;
                       std::size_t 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());
                       if (verbose)
                           taskstatus("copy %s to new dir : %s",
                                 fullSource.c_str(), fullDest.c_str());
                       if (!isNewerThan(fullSource, fullDest))
                           {
                           if (verbose)
                               taskstatus("copy skipping %s", fullSource.c_str());
                           continue;
                           }
                       if (!copyFile(fullSource, fullDest))
                           return false;
                       nrFiles++;
                       }
                   taskstatus("%d file(s) copied", nrFiles);
                   }
               else //file source
                   {
                   //For file->dir we want only the basename of
                   //the source appended to the dest dir
                   taskstatus("%s to %s", 
                       fileName.c_str(), toDirName.c_str());
                   String baseName = fileName;
                   std::size_t pos = baseName.find_last_of('/');
                   if (pos!=baseName.npos && pos<baseName.size()-1)
                       baseName = baseName.substr(pos+1, baseName.size());
                   String fullSource = parent.resolve(fileName);
                   String destPath;
                   if (toDirName.size()>0)
                       {
                       destPath.append(toDirName);
                       destPath.append("/");
                       }
                   destPath.append(baseName);
                   String fullDest = parent.resolve(destPath);
                   if (verbose)
                       taskstatus("file %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))
                       {
                       taskstatus("skipped");
                       return true;
                       }
                   if (!copyFile(fullSource, fullDest))
                       return false;
                   taskstatus("1 file copied");
                   }
               return true;
               }
           }
        return true;
        }


    virtual bool parse(Element *elem)
        {
        if (!parent.getAttribute(elem, "file", fileNameOpt))
            return false;
        if (!parent.getAttribute(elem, "tofile", toFileNameOpt))
            return false;
        if (toFileNameOpt.size() > 0)
            cptype = CP_TOFILE;
        if (!parent.getAttribute(elem, "todir", toDirNameOpt))
            return false;
        if (toDirNameOpt.size() > 0)
            cptype = CP_TODIR;
        if (!parent.getAttribute(elem, "verbose", verboseOpt))
            return false;
            
        haveFileSet = false;
        
        std::vector<Element *> children = elem->getChildren();
        for (std::size_t i=0 ; i<children.size() ; i++)
            {
            Element *child = children[i];
            String tagName = child->getName();
            if (tagName == "fileset")
                {
                if (!parseFileSet(child, parent, fileSet))
                    {
                    error("problem getting fileset");
                    return false;
                    }
                haveFileSet = true;
                }
            }

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

        return true;
        }
        
private:

    int cptype;
    bool haveFileSet;

    FileSet fileSet;
    String  fileNameOpt;
    String  toFileNameOpt;
    String  toDirNameOpt;
    String  verboseOpt;
};


/**
 * Generate CxxTest files
 */
class TaskCxxTestPart: public Task
{
public:

    TaskCxxTestPart(MakeBase &par) : Task(par)
         {
         type    = TASK_CXXTEST_PART;
         name    = "cxxtestpart";
         }

    virtual ~TaskCxxTestPart()
        {}

    virtual bool execute()
        {
        if (!listFiles(parent, fileSet))
            return false;
        String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
                
        String fullDest = parent.resolve(parent.eval(destPathOpt, "."));
        String cmd = parent.eval(commandOpt, "cxxtestgen.py");
        cmd.append(" --part -o ");
        cmd.append(fullDest);

        unsigned int newFiles = 0;
        for (std::size_t i=0 ; i<fileSet.size() ; i++)
            {
            String fileName = fileSet[i];
            if (getSuffix(fileName) != "h")
                continue;
            String sourcePath;
            if (fileSetDir.size()>0)
                {
                sourcePath.append(fileSetDir);
                sourcePath.append("/");
                }
            sourcePath.append(fileName);
            String fullSource = parent.resolve(sourcePath);

            cmd.append(" ");
            cmd.append(fullSource);
            if (isNewerThan(fullSource, fullDest)) newFiles++;
            }
        
        if (newFiles>0) {
            size_t const lastSlash = fullDest.find_last_of('/');
            if (lastSlash != fullDest.npos) {
                String directory(fullDest, 0, lastSlash);
                if (!createDirectory(directory))
                    return false;
            }

            String outString, errString;
            if (!executeCommand(cmd.c_str(), "", outString, errString))
                {
                error("<cxxtestpart> problem: %s", errString.c_str());
                return false;
                }
            removeFromStatCache(getNativePath(fullDest));
        }

        return true;
        }

    virtual bool parse(Element *elem)
        {
        if (!parent.getAttribute(elem, "command", commandOpt))
            return false;
        if (!parent.getAttribute(elem, "out", destPathOpt))
            return false;
            
        std::vector<Element *> children = elem->getChildren();
        for (std::size_t i=0 ; i<children.size() ; i++)
            {
            Element *child = children[i];
            String tagName = child->getName();
            if (tagName == "fileset")
                {
                if (!parseFileSet(child, parent, fileSet))
                    return false;
                }
            }
        return true;
        }

private:

    String  commandOpt;
    String  destPathOpt;
    FileSet fileSet;

};


/**
 * Generate the CxxTest root file
 */
class TaskCxxTestRoot: public Task
{
public:

    TaskCxxTestRoot(MakeBase &par) : Task(par)
         {
         type    = TASK_CXXTEST_ROOT;
         name    = "cxxtestroot";
         }

    virtual ~TaskCxxTestRoot()
        {}

    virtual bool execute()
        {
        if (!listFiles(parent, fileSet))
            return false;
        String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
        unsigned int newFiles = 0;
                
        String fullDest = parent.resolve(parent.eval(destPathOpt, "."));
        String cmd = parent.eval(commandOpt, "cxxtestgen.py");
        cmd.append(" --root -o ");
        cmd.append(fullDest);
        String templateFile = parent.eval(templateFileOpt, "");
        if (templateFile.size()>0) {
            String fullTemplate = parent.resolve(templateFile);
            cmd.append(" --template=");
            cmd.append(fullTemplate);
            if (isNewerThan(fullTemplate, fullDest)) newFiles++;
        }

        for (std::size_t i=0 ; i<fileSet.size() ; i++)
            {
            String fileName = fileSet[i];
            if (getSuffix(fileName) != "h")
                continue;
            String sourcePath;
            if (fileSetDir.size()>0)
                {
                sourcePath.append(fileSetDir);
                sourcePath.append("/");
                }
            sourcePath.append(fileName);
            String fullSource = parent.resolve(sourcePath);

            cmd.append(" ");
            cmd.append(fullSource);
            if (isNewerThan(fullSource, fullDest)) newFiles++;
            }
        
        if (newFiles>0) {
            size_t const lastSlash = fullDest.find_last_of('/');
            if (lastSlash != fullDest.npos) {
                String directory(fullDest, 0, lastSlash);
                if (!createDirectory(directory))
                    return false;
            }

            String outString, errString;
            if (!executeCommand(cmd.c_str(), "", outString, errString))
                {
                error("<cxxtestroot> problem: %s", errString.c_str());
                return false;
                }
            removeFromStatCache(getNativePath(fullDest));
        }

        return true;
        }

    virtual bool parse(Element *elem)
        {
        if (!parent.getAttribute(elem, "command", commandOpt))
            return false;
        if (!parent.getAttribute(elem, "template", templateFileOpt))
            return false;
        if (!parent.getAttribute(elem, "out", destPathOpt))
            return false;
            
        std::vector<Element *> children = elem->getChildren();
        for (std::size_t i=0 ; i<children.size() ; i++)
            {
            Element *child = children[i];
            String tagName = child->getName();
            if (tagName == "fileset")
                {
                if (!parseFileSet(child, parent, fileSet))
                    return false;
                }
            }
        return true;
        }

private:

    String  commandOpt;
    String  templateFileOpt;
    String  destPathOpt;
    FileSet fileSet;

};


/**
 * Execute the CxxTest test executable
 */
class TaskCxxTestRun: public Task
{
public:

    TaskCxxTestRun(MakeBase &par) : Task(par)
         {
         type    = TASK_CXXTEST_RUN;
         name    = "cxxtestrun";
         }

    virtual ~TaskCxxTestRun()
        {}

    virtual bool execute()
        {
        unsigned int newFiles = 0;
                
        String workingDir = parent.resolve(parent.eval(workingDirOpt, "inkscape"));
        String rawCmd = parent.eval(commandOpt, "build/cxxtests");

        String cmdExe;
        if (fileExists(rawCmd)) {
            cmdExe = rawCmd;
        } else if (fileExists(rawCmd + ".exe")) {
            cmdExe = rawCmd + ".exe";
        } else {
            error("<cxxtestrun> problem: cxxtests executable not found! (command=\"%s\")", rawCmd.c_str());
        }
        // Note that the log file names are based on the exact name used to call cxxtests (it uses argv[0] + ".log"/".xml")
        if (isNewerThan(cmdExe, rawCmd + ".log") || isNewerThan(cmdExe, rawCmd + ".xml")) newFiles++;

        // Prepend the necessary ../'s
        String cmd = rawCmd;
        unsigned int workingDirDepth = 0;
        bool wasSlash = true;
        for(size_t i=0; i<workingDir.size(); i++) {
            // This assumes no . and .. parts
            if (wasSlash && workingDir[i]!='/') workingDirDepth++;
            wasSlash = workingDir[i] == '/';
        }
        for(size_t i=0; i<workingDirDepth; i++) {
            cmd = "../" + cmd;
        }
        
        if (newFiles>0) {
            char olddir[1024];
            if (workingDir.size()>0) {
                // TODO: Double-check usage of getcwd and handle chdir errors
                getcwd(olddir, 1024);
                chdir(workingDir.c_str());
            }

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

            if (workingDir.size()>0) {
                // TODO: Handle errors?
                chdir(olddir);
            }

            removeFromStatCache(getNativePath(cmd + ".log"));
            removeFromStatCache(getNativePath(cmd + ".xml"));
        }

        return true;
        }

    virtual bool parse(Element *elem)
        {
        if (!parent.getAttribute(elem, "command", commandOpt))
            return false;
        if (!parent.getAttribute(elem, "workingdir", workingDirOpt))
            return false;
        return true;
        }

private:

    String  commandOpt;
    String  workingDirOpt;

};


/**
 *
 */
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;
        }

    virtual ~TaskDelete()
        {}

    virtual bool execute()
        {
        String dirName   = parent.eval(dirNameOpt, ".");
        String fileName  = parent.eval(fileNameOpt, ".");
        bool verbose     = parent.evalBool(verboseOpt, false);
        bool quiet       = parent.evalBool(quietOpt, false);
        bool failOnError = parent.evalBool(failOnErrorOpt, true);
        switch (delType)
            {
            case DEL_FILE:
                {
                taskstatus("file: %s", fileName.c_str());
                String fullName = parent.resolve(fileName);
                char *fname = (char *)fullName.c_str();
                if (!quiet && verbose)
                    taskstatus("path: %s", fname);
                if (failOnError && !removeFile(fullName))
                    {
                    //error("Could not delete file '%s'", fullName.c_str());
                    return false;
                    }
                return true;
                }
            case DEL_DIR:
                {
                taskstatus("dir: %s", dirName.c_str());
                String fullDir = parent.resolve(dirName);
                if (!quiet && verbose)
                    taskstatus("path: %s", fullDir.c_str());
                if (failOnError && !removeDirectory(fullDir))
                    {
                    //error("Could not delete directory '%s'", fullDir.c_str());
                    return false;
                    }
                return true;
                }
            }
        return true;
        }

    virtual bool parse(Element *elem)
        {
        if (!parent.getAttribute(elem, "file", fileNameOpt))
            return false;
        if (fileNameOpt.size() > 0)
            delType = DEL_FILE;
        if (!parent.getAttribute(elem, "dir", dirNameOpt))
            return false;
        if (dirNameOpt.size() > 0)
            delType = DEL_DIR;
        if (fileNameOpt.size()>0 && dirNameOpt.size()>0)
            {
            error("<delete> can have one attribute of file= or dir=");
            return false;
            }
        if (fileNameOpt.size()==0 && dirNameOpt.size()==0)
            {
            error("<delete> must have one attribute of file= or dir=");
            return false;
            }
        if (!parent.getAttribute(elem, "verbose", verboseOpt))
            return false;
        if (!parent.getAttribute(elem, "quiet", quietOpt))
            return false;
        if (!parent.getAttribute(elem, "failonerror", failOnErrorOpt))
            return false;
        return true;
        }

private:

    int delType;
    String dirNameOpt;
    String fileNameOpt;
    String verboseOpt;
    String quietOpt;
    String failOnErrorOpt;
};


/**
 * Send a message to stdout
 */
class TaskEcho : public Task
{
public:

    TaskEcho(MakeBase &par) : Task(par)
        { type = TASK_ECHO; name = "echo"; }

    virtual ~TaskEcho()
        {}

    virtual bool execute()
        {
        //let message have priority over text
        String message = parent.eval(messageOpt, "");
        String text    = parent.eval(textOpt, "");
        if (message.size() > 0)
            {
            fprintf(stdout, "%s\n", message.c_str());
            }
        else if (text.size() > 0)
            {
            fprintf(stdout, "%s\n", text.c_str());
            }
        return true;
        }

    virtual bool parse(Element *elem)
        {
        if (!parent.getValue(elem, textOpt))
            return false;
        textOpt    = leftJustify(textOpt);
        if (!parent.getAttribute(elem, "message", messageOpt))
            return false;
        return true;
        }

private:

    String messageOpt;
    String textOpt;
};



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

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

    virtual ~TaskJar()
        {}

    virtual bool execute()
        {
        String command  = parent.eval(commandOpt, "jar");
        String basedir  = parent.eval(basedirOpt, ".");
        String destfile = parent.eval(destfileOpt, ".");

        String cmd = command;
        cmd.append(" -cf ");
        cmd.append(destfile);
        cmd.append(" -C ");
        cmd.append(basedir);
        cmd.append(" .");

        String execCmd = cmd;

        String outString, errString;
        bool ret = executeCommand(execCmd.c_str(), "", outString, errString);
        if (!ret)
            {
            error("<jar> command '%s' failed :\n %s",
                                      execCmd.c_str(), errString.c_str());
            return false;
            }
        removeFromStatCache(getNativePath(destfile));
        return true;
        }

    virtual bool parse(Element *elem)
        {
        if (!parent.getAttribute(elem, "command", commandOpt))
            return false;
        if (!parent.getAttribute(elem, "basedir", basedirOpt))
            return false;
        if (!parent.getAttribute(elem, "destfile", destfileOpt))
            return false;
        if (basedirOpt.size() == 0 || destfileOpt.size() == 0)
            {
            error("<jar> required both basedir and destfile attributes to be set");
            return false;
            }
        return true;
        }

private:

    String commandOpt;
    String basedirOpt;
    String destfileOpt;
};


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

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

    virtual ~TaskJavac()
        {}

    virtual bool execute()
        {
        String command  = parent.eval(commandOpt, "javac");
        String srcdir   = parent.eval(srcdirOpt, ".");
        String destdir  = parent.eval(destdirOpt, ".");
        String target   = parent.eval(targetOpt, "");

        std::vector<String> fileList;
        if (!listFiles(srcdir, "", fileList))
            {
            return false;
            }
        String cmd = command;
        cmd.append(" -d ");
        cmd.append(destdir);
        cmd.append(" -classpath ");
        cmd.append(destdir);
        cmd.append(" -sourcepath ");
        cmd.append(srcdir);
        cmd.append(" ");
        if (target.size()>0)
            {
            cmd.append(" -target ");
            cmd.append(target);
            cmd.append(" ");
            }
        String fname = "javalist.btool";
        FILE *f = fopen(fname.c_str(), "w");
        int count = 0;
        for (std::size_t i=0 ; i<fileList.size() ; i++)
            {
            String fname = fileList[i];
            String srcName = fname;
            if (fname.size()<6) //x.java
                continue;
            if (fname.compare(fname.size()-5, 5, ".java") != 0)
                continue;
            String baseName = fname.substr(0, fname.size()-5);
            String destName = baseName;
            destName.append(".class");

            String fullSrc = srcdir;
            fullSrc.append("/");
            fullSrc.append(fname);
            String fullDest = destdir;
            fullDest.append("/");
            fullDest.append(destName);
            //trace("fullsrc:%s fulldest:%s", fullSrc.c_str(), fullDest.c_str());
            if (!isNewerThan(fullSrc, fullDest))
                continue;

            count++;
            fprintf(f, "%s\n", fullSrc.c_str());
            }
        fclose(f);
        if (!count)
            {
            taskstatus("nothing to do");
            return true;
            }

        taskstatus("compiling %d files", count);

        String execCmd = cmd;
        execCmd.append("@");
        execCmd.append(fname);

        String outString, errString;
        bool ret = executeCommand(execCmd.c_str(), "", outString, errString);
        if (!ret)
            {
            error("<javac> command '%s' failed :\n %s",
                                      execCmd.c_str(), errString.c_str());
            return false;
            }
        // TODO: 
        //removeFromStatCache(getNativePath(........));
        return true;
        }

    virtual bool parse(Element *elem)
        {
        if (!parent.getAttribute(elem, "command", commandOpt))
            return false;
        if (!parent.getAttribute(elem, "srcdir", srcdirOpt))
            return false;
        if (!parent.getAttribute(elem, "destdir", destdirOpt))
            return false;
        if (srcdirOpt.size() == 0 || destdirOpt.size() == 0)
            {
            error("<javac> required both srcdir and destdir attributes to be set");
            return false;
            }
        if (!parent.getAttribute(elem, "target", targetOpt))
            return false;
        return true;
        }

private:

    String commandOpt;
    String srcdirOpt;
    String destdirOpt;
    String targetOpt;

};


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

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

    virtual ~TaskLink()
        {}

    virtual void UniqueParams(std::string& source) {
        size_t prev = 0;
        size_t next = 0;
        std::list<std::string> thelist;
        std::list<std::string>::iterator it;
        std::string tstring=" ";
        source +=std::string(" "); // else the last token may be lost
	while ((next = source.find_first_of(" ", prev)) != std::string::npos){
	   if (next - prev != 0){
	      thelist.push_back(source.substr(prev, next - prev));
	   }
	   prev = next + 1;
	}
        thelist.sort();
        source.clear();
        source +=std::string(" ");
        for(it=thelist.begin(); it!=thelist.end();it++){
        	if(*it != tstring){
        		tstring = *it;
        		source +=tstring;
        		source +=std::string(" ");
        	}
        }
     }

    virtual bool execute()
        {
        String  command        = parent.eval(commandOpt, "g++");
        String  fileName       = parent.eval(fileNameOpt, "");
        String  flags          = parent.eval(flagsOpt, "");
        String  libs           = parent.eval(libsOpt, "");
        bool    doStrip        = parent.evalBool(doStripOpt, false);
        String  symFileName    = parent.eval(symFileNameOpt, "");
        String  stripCommand   = parent.eval(stripCommandOpt, "strip");
        String  objcopyCommand = parent.eval(objcopyCommandOpt, "objcopy");

        if (!listFiles(parent, fileSet))
            return false;
        String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
        //trace("%d files in %s", fileSet.size(), fileSetDir.c_str());
        bool doit = false;
        String fullTarget = parent.resolve(fileName);
        String cmd = command;
        cmd.append(" -o ");
        cmd.append(fullTarget);
        cmd.append(" ");
        cmd.append(flags);
        for (std::size_t i=0 ; i<fileSet.size() ; i++)
            {
            cmd.append(" ");
            String obj;
            if (fileSetDir.size()>0)
                {
                obj.append(fileSetDir);
                obj.append("/");
                }
            obj.append(fileSet[i]);
            String fullObj = parent.resolve(obj);
            String nativeFullObj = getNativePath(fullObj);
            cmd.append(nativeFullObj);
            //trace("link: tgt:%s obj:%s", fullTarget.c_str(),
            //          fullObj.c_str());
            if (isNewerThan(fullObj, fullTarget))
                doit = true;
            }
        cmd.append(" ");
        // trim it down to unique elements, reduce command line size
        UniqueParams(libs);
        cmd.append(libs);
        if (!doit)
            {
            //trace("link not needed");
            return true;
            }
        //trace("LINK cmd:%s", cmd.c_str());


        String outbuf, errbuf;
        // std::cout << "DEBUG command = " << cmd << std::endl;
        if (!executeCommand(cmd.c_str(), "", outbuf, errbuf))
            {
            error("LINK problem: %s", errbuf.c_str());
            return false;
            }
        removeFromStatCache(getNativePath(fullTarget));

        if (symFileName.size()>0)
            {
            String symFullName = parent.resolve(symFileName);
            cmd = objcopyCommand;
            cmd.append(" --only-keep-debug ");
            cmd.append(getNativePath(fullTarget));
            cmd.append(" ");
            cmd.append(getNativePath(symFullName));
            if (!executeCommand(cmd, "", outbuf, errbuf))
                {
                error("<strip> symbol file failed : %s", errbuf.c_str());
                return false;
                }
            removeFromStatCache(getNativePath(symFullName));
            }
            
        if (doStrip)
            {
            cmd = stripCommand;
            cmd.append(" ");
            cmd.append(getNativePath(fullTarget));
            if (!executeCommand(cmd, "", outbuf, errbuf))
               {
               error("<strip> failed : %s", errbuf.c_str());
               return false;
               }
            removeFromStatCache(getNativePath(fullTarget));
            }

        return true;
        }

    virtual bool parse(Element *elem)
        {
        if (!parent.getAttribute(elem, "command", commandOpt))
            return false;
        if (!parent.getAttribute(elem, "objcopycommand", objcopyCommandOpt))
            return false;
        if (!parent.getAttribute(elem, "stripcommand", stripCommandOpt))
            return false;
        if (!parent.getAttribute(elem, "out", fileNameOpt))
            return false;
        if (!parent.getAttribute(elem, "strip", doStripOpt))
            return false;
        if (!parent.getAttribute(elem, "symfile", symFileNameOpt))
            return false;
            
        std::vector<Element *> children = elem->getChildren();
        for (std::size_t i=0 ; i<children.size() ; i++)
            {
            Element *child = children[i];
            String tagName = child->getName();
            if (tagName == "fileset")
                {
                if (!parseFileSet(child, parent, fileSet))
                    return false;
                }
            else if (tagName == "flags")
                {
                if (!parent.getValue(child, flagsOpt))
                    return false;
                flagsOpt = strip(flagsOpt);
                }
            else if (tagName == "libs")
                {
                if (!parent.getValue(child, libsOpt))
                    return false;
                libsOpt = strip(libsOpt);
                }
            }
        return true;
        }

private:

    FileSet fileSet;

    String  commandOpt;
    String  fileNameOpt;
    String  flagsOpt;
    String  libsOpt;
    String  doStripOpt;
    String  symFileNameOpt;
    String  stripCommandOpt;
    String  objcopyCommandOpt;

};



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

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

    virtual ~TaskMakeFile()
        {}

    virtual bool execute()
        {
        String fileName = parent.eval(fileNameOpt, "");
        bool force      = parent.evalBool(forceOpt, false);
        String text     = parent.eval(textOpt, "");

        taskstatus("%s", fileName.c_str());
        String fullName = parent.resolve(fileName);
        if (!force && !isNewerThan(parent.getURI().getPath(), fullName))
            {
            taskstatus("skipped");
            return true;
            }
        String fullNative = getNativePath(fullName);
        //trace("fullName:%s", fullName.c_str());
        FILE *f = fopen(fullNative.c_str(), "w");
        if (!f)
            {
            error("<makefile> could not open %s for writing : %s",
                fullName.c_str(), strerror(errno));
            return false;
            }
        for (std::size_t i=0 ; i<text.size() ; i++)
            fputc(text[i], f);
        fputc('\n', f);
        fclose(f);
        removeFromStatCache(fullNative);
        return true;
        }

    virtual bool parse(Element *elem)
        {
        if (!parent.getAttribute(elem, "file", fileNameOpt))
            return false;
        if (!parent.getAttribute(elem, "force", forceOpt))
            return false;
        if (fileNameOpt.size() == 0)
            {
            error("<makefile> requires 'file=\"filename\"' attribute");
            return false;
            }
        if (!parent.getValue(elem, textOpt))
            return false;
        textOpt = leftJustify(textOpt);
        //trace("dirname:%s", dirName.c_str());
        return true;
        }

private:

    String fileNameOpt;
    String forceOpt;
    String textOpt;
};



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

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

    virtual ~TaskMkDir()
        {}

    virtual bool execute()
        {
        String dirName = parent.eval(dirNameOpt, ".");
        
        taskstatus("%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", dirNameOpt))
            return false;
        if (dirNameOpt.size() == 0)
            {
            error("<mkdir> requires 'dir=\"dirname\"' attribute");
            return false;
            }
        return true;
        }

private:

    String dirNameOpt;
};



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

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

    virtual ~TaskMsgFmt()
        {}

    virtual bool execute()
        {
        String  command   = parent.eval(commandOpt, "msgfmt");
        String  toDirName = parent.eval(toDirNameOpt, ".");
        String  outName   = parent.eval(outNameOpt, "");
        bool    owndir    = parent.evalBool(owndirOpt, false);

        if (!listFiles(parent, fileSet))
            return false;
        String fileSetDir = fileSet.getDirectory();

        //trace("msgfmt: %d", fileSet.size());
        for (std::size_t i=0 ; i<fileSet.size() ; i++)
            {
            String fileName = fileSet[i];
            if (getSuffix(fileName) != "po")
                continue;
            String sourcePath;
            if (fileSetDir.size()>0)
                {
                sourcePath.append(fileSetDir);
                sourcePath.append("/");
                }
            sourcePath.append(fileName);
            String fullSource = parent.resolve(sourcePath);

            String destPath;
            if (toDirName.size()>0)
                {
                destPath.append(toDirName);
                destPath.append("/");
                }
            if (owndir)
                {
                String subdir = fileName;
                std::size_t pos = subdir.find_last_of('.');
                if (pos != subdir.npos)
                    subdir = subdir.substr(0, pos);
                destPath.append(subdir);
                destPath.append("/");
                }
            //Pick the output file name
            if (outName.size() > 0)
                {
                destPath.append(outName);
                }
            else
                {
                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("<msgfmt> problem: %s", errString.c_str());
                return false;
                }
            removeFromStatCache(getNativePath(fullDest));
            }

        return true;
        }

    virtual bool parse(Element *elem)
        {
        if (!parent.getAttribute(elem, "command", commandOpt))
            return false;
        if (!parent.getAttribute(elem, "todir", toDirNameOpt))
            return false;
        if (!parent.getAttribute(elem, "out", outNameOpt))
            return false;
        if (!parent.getAttribute(elem, "owndir", owndirOpt))
            return false;
            
        std::vector<Element *> children = elem->getChildren();
        for (std::size_t i=0 ; i<children.size() ; i++)
            {
            Element *child = children[i];
            String tagName = child->getName();
            if (tagName == "fileset")
                {
                if (!parseFileSet(child, parent, fileSet))
                    return false;
                }
            }
        return true;
        }

private:

    FileSet fileSet;

    String  commandOpt;
    String  toDirNameOpt;
    String  outNameOpt;
    String  owndirOpt;

};



/**
 *  Perform a Package-Config query similar to pkg-config
 */
class TaskPkgConfig : public Task
{
public:

    typedef enum
        {
        PKG_CONFIG_QUERY_CFLAGS,
        PKG_CONFIG_QUERY_LIBS,
        PKG_CONFIG_QUERY_ALL
        } QueryTypes;

    TaskPkgConfig(MakeBase &par) : Task(par)
        {
        type = TASK_PKG_CONFIG;
        name = "pkg-config";
        }

    virtual ~TaskPkgConfig()
        {}

    virtual bool execute()
        {
        String pkgName       = parent.eval(pkgNameOpt,      "");
        String prefix        = parent.eval(prefixOpt,       "");
        String propName      = parent.eval(propNameOpt,     "");
        String pkgConfigPath = parent.eval(pkgConfigPathOpt,"");
        String query         = parent.eval(queryOpt,        "all");

        String path = parent.resolve(pkgConfigPath);
        PkgConfig pkgconfig;
        pkgconfig.setPath(path);
        pkgconfig.setPrefix(prefix);
        if (!pkgconfig.query(pkgName))
            {
            error("<pkg-config> query failed for '%s", name.c_str());
            return false;
            }
            
        String val = "";
        if (query == "cflags")
            val = pkgconfig.getCflags();
        else if (query == "libs")
            val =pkgconfig.getLibs();
        else if (query == "all")
            val = pkgconfig.getAll();
        else
            {
            error("<pkg-config> unhandled query : %s", query.c_str());
            return false;
            }
        taskstatus("property %s = '%s'", propName.c_str(), val.c_str());
        parent.setProperty(propName, val);
        return true;
        }

    virtual bool parse(Element *elem)
        {
        //# NAME
        if (!parent.getAttribute(elem, "name", pkgNameOpt))
            return false;
        if (pkgNameOpt.size()==0)
            {
            error("<pkg-config> requires 'name=\"package\"' attribute");
            return false;
            }

        //# PROPERTY
        if (!parent.getAttribute(elem, "property", propNameOpt))
            return false;
        if (propNameOpt.size()==0)
            {
            error("<pkg-config> requires 'property=\"name\"' attribute");
            return false;
            }
        //# PATH
        if (!parent.getAttribute(elem, "path", pkgConfigPathOpt))
            return false;
        //# PREFIX
        if (!parent.getAttribute(elem, "prefix", prefixOpt))
            return false;
        //# QUERY
        if (!parent.getAttribute(elem, "query", queryOpt))
            return false;

        return true;
        }

private:

    String queryOpt;
    String pkgNameOpt;
    String prefixOpt;
    String propNameOpt;
    String pkgConfigPathOpt;

};






/**
 *  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 fileName = parent.eval(fileNameOpt, "");
        String command  = parent.eval(commandOpt, "ranlib");

        String fullName = parent.resolve(fileName);
        //trace("fullDir:%s", fullDir.c_str());
        String cmd = command;
        cmd.append(" ");
        cmd.append(fullName);
        String outbuf, errbuf;
        if (!executeCommand(cmd, "", outbuf, errbuf))
            return false;
        // TODO:
        //removeFromStatCache(getNativePath(fullDest));
        return true;
        }

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

private:

    String fileNameOpt;
    String commandOpt;
};



/**
 * Compile a resource file into a binary object
 */
class TaskRC : public Task
{
public:

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

    virtual ~TaskRC()
        {}

    virtual bool execute()
        {
        String command  = parent.eval(commandOpt,  "windres");
        String flags    = parent.eval(flagsOpt,    "");
        String fileName = parent.eval(fileNameOpt, "");
        String outName  = parent.eval(outNameOpt,  "");

        String fullFile = parent.resolve(fileName);
        String fullOut  = parent.resolve(outName);
        if (!isNewerThan(fullFile, fullOut))
            return true;
        String cmd = command;
        cmd.append(" -o ");
        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;
            }
        removeFromStatCache(getNativePath(fullOut));
        return true;
        }

    virtual bool parse(Element *elem)
        {
        if (!parent.getAttribute(elem, "command", commandOpt))
            return false;
        if (!parent.getAttribute(elem, "file", fileNameOpt))
            return false;
        if (!parent.getAttribute(elem, "out", outNameOpt))
            return false;
        std::vector<Element *> children = elem->getChildren();
        for (std::size_t i=0 ; i<children.size() ; i++)
            {
            Element *child = children[i];
            String tagName = child->getName();
            if (tagName == "flags")
                {
                if (!parent.getValue(child, flagsOpt))
                    return false;
                }
            }
        return true;
        }

private:

    String commandOpt;
    String flagsOpt;
    String fileNameOpt;
    String outNameOpt;

};



/**
 *  Collect .o's into a .so or DLL
 */
class TaskSharedLib : public Task
{
public:

    TaskSharedLib(MakeBase &par) : Task(par)
        { type = TASK_SHAREDLIB; name = "dll"; }

    virtual ~TaskSharedLib()
        {}

    virtual bool execute()
        {
        String command     = parent.eval(commandOpt, "dllwrap");
        String fileName    = parent.eval(fileNameOpt, "");
        String defFileName = parent.eval(defFileNameOpt, "");
        String impFileName = parent.eval(impFileNameOpt, "");
        String libs        = parent.eval(libsOpt, "");

        //trace("###########HERE %d", fileSet.size());
        bool doit = false;
        
        String fullOut = parent.resolve(fileName);
        //trace("ar fullout: %s", fullOut.c_str());
        
        if (!listFiles(parent, fileSet))
            return false;
        String fileSetDir = parent.eval(fileSet.getDirectory(), ".");

        for (std::size_t i=0 ; i<fileSet.size() ; i++)
            {
            String fname;
            if (fileSetDir.size()>0)
                {
                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 = "dllwrap";
        cmd.append(" -o ");
        cmd.append(fullOut);
        if (defFileName.size()>0)
            {
            cmd.append(" --def ");
            cmd.append(defFileName);
            cmd.append(" ");
            }
        if (impFileName.size()>0)
            {
            cmd.append(" --implib ");
            cmd.append(impFileName);
            cmd.append(" ");
            }
        for (std::size_t i=0 ; i<fileSet.size() ; i++)
            {
            String fname;
            if (fileSetDir.size()>0)
                {
                fname.append(fileSetDir);
                fname.append("/");
                }
            fname.append(fileSet[i]);
            String fullName = parent.resolve(fname);

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

        String outString, errString;
        if (!executeCommand(cmd.c_str(), "", outString, errString))
            {
            error("<sharedlib> problem: %s", errString.c_str());
            return false;
            }
        removeFromStatCache(getNativePath(fullOut));
        return true;
        }

    virtual bool parse(Element *elem)
        {
        if (!parent.getAttribute(elem, "command", commandOpt))
            return false;
        if (!parent.getAttribute(elem, "file", fileNameOpt))
            return false;
        if (!parent.getAttribute(elem, "import", impFileNameOpt))
            return false;
        if (!parent.getAttribute(elem, "def", defFileNameOpt))
            return false;
            
        std::vector<Element *> children = elem->getChildren();
        for (std::size_t i=0 ; i<children.size() ; i++)
            {
            Element *child = children[i];
            String tagName = child->getName();
            if (tagName == "fileset")
                {
                if (!parseFileSet(child, parent, fileSet))
                    return false;
                }
            else if (tagName == "libs")
                {
                if (!parent.getValue(child, libsOpt))
                    return false;
                libsOpt = strip(libsOpt);
                }
            }
        return true;
        }

private:

    FileSet fileSet;

    String commandOpt;
    String fileNameOpt;
    String defFileNameOpt;
    String impFileNameOpt;
    String libsOpt;

};



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

    TaskStaticLib(MakeBase &par) : Task(par)
        { type = TASK_STATICLIB; name = "staticlib"; }

    virtual ~TaskStaticLib()
        {}

    virtual bool execute()
        {
        String command = parent.eval(commandOpt, "ar crv");
        String fileName = parent.eval(fileNameOpt, "");

        bool doit = false;
        
        String fullOut = parent.resolve(fileName);
        //trace("ar fullout: %s", fullOut.c_str());
        
        if (!listFiles(parent, fileSet))
            return false;
        String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
        //trace("###########HERE %s", fileSetDir.c_str());

        for (std::size_t i=0 ; i<fileSet.size() ; i++)
            {
            String fname;
            if (fileSetDir.size()>0)
                {
                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 (std::size_t i=0 ; i<fileSet.size() ; i++)
            {
            String fname;
            if (fileSetDir.size()>0)
                {
                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("<staticlib> problem: %s", errString.c_str());
            return false;
            }
        removeFromStatCache(getNativePath(fullOut));
        return true;
        }


    virtual bool parse(Element *elem)
        {
        if (!parent.getAttribute(elem, "command", commandOpt))
            return false;
        if (!parent.getAttribute(elem, "file", fileNameOpt))
            return false;
            
        std::vector<Element *> children = elem->getChildren();
        for (std::size_t i=0 ; i<children.size() ; i++)
            {
            Element *child = children[i];
            String tagName = child->getName();
            if (tagName == "fileset")
                {
                if (!parseFileSet(child, parent, fileSet))
                    return false;
                }
            }
        return true;
        }

private:

    FileSet fileSet;

    String commandOpt;
    String fileNameOpt;

};




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

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

    virtual ~TaskStrip()
        {}

    virtual bool execute()
        {
        String command     = parent.eval(commandOpt, "strip");
        String fileName    = parent.eval(fileNameOpt, "");
        String symFileName = parent.eval(symFileNameOpt, "");

        String fullName = parent.resolve(fileName);
        //trace("fullDir:%s", fullDir.c_str());
        String cmd;
        String outbuf, errbuf;

        if (symFileName.size()>0)
            {
            String symFullName = parent.resolve(symFileName);
            cmd = "objcopy --only-keep-debug ";
            cmd.append(getNativePath(fullName));
            cmd.append(" ");
            cmd.append(getNativePath(symFullName));
            if (!executeCommand(cmd, "", outbuf, errbuf))
                {
                error("<strip> symbol file failed : %s", errbuf.c_str());
                return false;
                }
            }
            
        cmd = command;
        cmd.append(getNativePath(fullName));
       if (!executeCommand(cmd, "", outbuf, errbuf))
            {
            error("<strip> failed : %s", errbuf.c_str());
            return false;
            }
        removeFromStatCache(getNativePath(fullName));
        return true;
        }

    virtual bool parse(Element *elem)
        {
        if (!parent.getAttribute(elem, "command", commandOpt))
            return false;
        if (!parent.getAttribute(elem, "file", fileNameOpt))
            return false;
        if (!parent.getAttribute(elem, "symfile", symFileNameOpt))
            return false;
        if (fileNameOpt.size() == 0)
            {
            error("<strip> requires 'file=\"fileName\"' attribute");
            return false;
            }
        return true;
        }

private:

    String commandOpt;
    String fileNameOpt;
    String symFileNameOpt;
};


/**
 *
 */
class TaskTouch : public Task
{
public:

    TaskTouch(MakeBase &par) : Task(par)
        { type = TASK_TOUCH; name = "touch"; }

    virtual ~TaskTouch()
        {}

    virtual bool execute()
        {
        String fileName = parent.eval(fileNameOpt, "");

        String fullName = parent.resolve(fileName);
        String nativeFile = getNativePath(fullName);
        if (!isRegularFile(fullName) && !isDirectory(fullName))
            {            
            // S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH
            int ret = creat(nativeFile.c_str(), 0666);
            if (ret != 0) 
                {
                error("<touch> could not create '%s' : %s",
                    nativeFile.c_str(), strerror(ret));
                return false;
                }
            return true;
            }
        int ret = utime(nativeFile.c_str(), (struct utimbuf *)0);
        if (ret != 0)
            {
            error("<touch> could not update the modification time for '%s' : %s",
                nativeFile.c_str(), strerror(ret));
            return false;
            }
        removeFromStatCache(nativeFile);
        return true;
        }

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

    String fileNameOpt;
};


/**
 *
 */
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, int lineNr)
{
    String tagName = elem->getName();
    //trace("task:%s", tagName.c_str());
    Task *task = NULL;
    if (tagName == "cc")
        task = new TaskCC(parent);
    else if (tagName == "copy")
        task = new TaskCopy(parent);
    else if (tagName == "cxxtestpart")
        task = new TaskCxxTestPart(parent);
    else if (tagName == "cxxtestroot")
        task = new TaskCxxTestRoot(parent);
    else if (tagName == "cxxtestrun")
        task = new TaskCxxTestRun(parent);
    else if (tagName == "delete")
        task = new TaskDelete(parent);
    else if (tagName == "echo")
        task = new TaskEcho(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 == "pkg-config")
        task = new TaskPkgConfig(parent);
    else if (tagName == "ranlib")
        task = new TaskRanlib(parent);
    else if (tagName == "rc")
        task = new TaskRC(parent);
    else if (tagName == "sharedlib")
        task = new TaskSharedLib(parent);
    else if (tagName == "staticlib")
        task = new TaskStaticLib(parent);
    else if (tagName == "strip")
        task = new TaskStrip(parent);
    else if (tagName == "touch")
        task = new TaskTouch(parent);
    else if (tagName == "tstamp")
        task = new TaskTstamp(parent);
    else
        {
        error("Unknown task '%s'", tagName.c_str());
        return NULL;
        }

    task->setLine(lineNr);

    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<String> &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<Task *> &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<String> deps;

    std::vector<Task *> 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<String, Target> &getTargets()
        { return targets; }


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

    /**
     * Overload a <property>
     */
    virtual bool specifyProperty(const String &name,
                                 const String &value);

    /**
     *
     */
    virtual bool run();

    /**
     *
     */
    virtual 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<String> &targetsCompleted);


    /**
     *
     */
    bool execute();

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

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

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

    /**
     *
     */
    bool parseFile();

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


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

    String projectName;

    String currentTarget;

    String defaultTarget;

    String specifiedTarget;

    String baseDir;

    String description;
    
    //std::vector<Property> properties;
    
    std::map<String, Target> targets;

    std::vector<Task *> allTasks;
    
    std::map<String, String> specifiedProperties;

};


//########################################################################
//# 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     = "";
    envPrefix       = "env.";
    pcPrefix        = "pc.";
    pccPrefix       = "pcc.";
    pclPrefix       = "pcl.";
    bzrPrefix       = "bzr.";
    properties.clear();
    for (std::size_t i = 0 ; i < allTasks.size() ; i++)
        delete allTasks[i];
    allTasks.clear();
}



/**
 *
 */
void Make::cleanup()
{
    for (std::size_t 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<String> Make::glob(const String &pattern)
{
    std::vector<String> res;
    return res;
}


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



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

    String name = target.getName();

    //First get any dependencies for this target
    std::vector<String> deps = target.getDependencies();
    for (std::size_t i=0 ; i<deps.size() ; i++)
        {
        String dep = deps[i];
        //Did we do it already?  Skip
        if (targetsCompleted.find(dep)!=targetsCompleted.end())
            continue;
            
        std::map<String, Target> &tgts =
               target.getParent().getTargets();
        std::map<String, Target>::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\n##### %s", name.c_str(),
            target.getDescription().c_str());

    //Now let's do the tasks
    std::vector<Task *> &tasks = target.getTasks();
    for (std::size_t i=0 ; i<tasks.size() ; i++)
        {
        Task *task = tasks[i];
        status("--- %s / %s", name.c_str(), task->getName().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<String, Target>::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<String> targetsCompleted;
    if (!executeTarget(target, targetsCompleted))
        {
        return false;
        }

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




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

    std::vector<String> deps = target.getDependencies();
    for (std::size_t i=0 ; i<deps.size() ; i++)
        {
        String dep = deps[i];
        //First thing entered was the starting Target
        if (dep == depList[0])
            {
            error("Circular dependency '%s' found at '%s'",
                      dep.c_str(), tgtName.c_str());
            std::vector<String>::iterator diter;
            for (diter=depList.begin() ; diter!=depList.end() ; diter++)
                {
                error("  %s", diter->c_str());
                }
            return false;
            }

        std::map<String, Target> &tgts =
                  target.getParent().getTargets();
        std::map<String, Target>::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);
            fclose(f);
			return false;
            }
        if (prefix.size() > 0)
            {
            key.insert(0, prefix);
            }

        //skip whitespace
        for (p=p2 ; p<len ; p++)
            if (!isspace(s[p]))
                break;

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

        //skip whitespace
        for ( ; p<len ; p++)
            if (!isspace(s[p]))
                break;

        /* This way expects a word after the =
        p2 = getword(p, s, val);
        if (p2 <= p)
            {
            error("property file %s, line %d: expected value",
                    fileName.c_str(), linenr);
            return false;
            }
        */
        // This way gets the rest of the line after the =
        if (p>=len)
            {
            error("property file %s, line %d: expected value",
                    fileName.c_str(), linenr);
            return false;
            }
        val = s.substr(p);
        if (key.size()==0)
            continue;
        //allow property to be set, even if val=""

        //trace("key:'%s' val:'%s'", key.c_str(), val.c_str());
        //See if we wanted to overload this property
        std::map<String, String>::iterator iter =
            specifiedProperties.find(key);
        if (iter!=specifiedProperties.end())
            {
            val = iter->second;
            status("overloading property '%s' = '%s'",
                   key.c_str(), val.c_str());
            }
        properties[key] = val;
        }
    fclose(f);
    return true;
}




/**
 *
 */
bool Make::parseProperty(Element *elem)
{
    std::vector<Attribute> &attrs = elem->getAttributes();
    for (std::size_t i=0 ; i<attrs.size() ; i++)
        {
        String attrName = attrs[i].getName();
        String attrVal  = attrs[i].getValue();

        if (attrName == "name")
            {
            String val;
            if (!getAttribute(elem, "value", val))
                return false;
            if (val.size() > 0)
                {
                properties[attrVal] = val;
                }
            else
                {
                if (!getAttribute(elem, "location", val))
                    return false;
                //let the property exist, even if not defined
                properties[attrVal] = val;
                }
            //See if we wanted to overload this property
            std::map<String, String>::iterator iter =
                specifiedProperties.find(attrVal);
            if (iter != specifiedProperties.end())
                {
                val = iter->second;
                status("overloading property '%s' = '%s'",
                    attrVal.c_str(), val.c_str());
                properties[attrVal] = val;
                }
            }
        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 (attrVal.find('.') != attrVal.npos)
                {
                error("environment prefix cannot have a '.' in it");
                return false;
                }
            envPrefix = attrVal;
            envPrefix.push_back('.');
            }
        else if (attrName == "pkg-config")
            {
            if (attrVal.find('.') != attrVal.npos)
                {
                error("pkg-config prefix cannot have a '.' in it");
                return false;
                }
            pcPrefix = attrVal;
            pcPrefix.push_back('.');
            }
        else if (attrName == "pkg-config-cflags")
            {
            if (attrVal.find('.') != attrVal.npos)
                {
                error("pkg-config-cflags prefix cannot have a '.' in it");
                return false;
                }
            pccPrefix = attrVal;
            pccPrefix.push_back('.');
            }
        else if (attrName == "pkg-config-libs")
            {
            if (attrVal.find('.') != attrVal.npos)
                {
                error("pkg-config-libs prefix cannot have a '.' in it");
                return false;
                }
            pclPrefix = attrVal;
            pclPrefix.push_back('.');
            }
        else if (attrName == "subversion")
            {
            if (attrVal.find('.') != attrVal.npos)
                {
                error("bzr prefix cannot have a '.' in it");
                return false;
                }
            bzrPrefix = attrVal;
            bzrPrefix.push_back('.');
            }
        }

    return true;
}




/**
 *
 */
bool Make::parseFile()
{
    status("######## PARSE : %s", uri.getPath().c_str());

    setLine(0);

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

    if (root->getChildren().size()==0 ||
        root->getChildren()[0]->getName()!="project")
        {
        error("Main xml element should be <project>");
        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<Element *> children = project->getChildren();
    for (std::size_t i=0 ; i<children.size() ; i++)
        {
        Element *elem = children[i];
        setLine(elem->getLine());
        String tagName = elem->getName();

        //########## 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<Element *> telems = elem->getChildren();
            for (std::size_t i=0 ; i<telems.size() ; i++)
                {
                Element *telem = telems[i];
                Task breeder(*this);
                Task *task = breeder.createTask(telem, telem->getLine());
                if (!task)
                    return false;
                allTasks.push_back(task);
                target.addTask(task);
                }

            //Check name
            if (tname.size() == 0)
                {
                error("no name for target");
                return false;
                }
            //Check for duplicate name
            if (targets.find(tname) != targets.end())
                {
                error("target '%s' already defined", tname.c_str());
                return false;
                }
            //more work than targets[tname]=target, but avoids default allocator
            targets.insert(std::make_pair<String, Target>(tname, target));
            }
        //######### none of the above
        else
            {
            error("unknown toplevel tag: <%s>", tagName.c_str());
            return false;
            }

        }

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


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


/**
 * Overload a <property>
 */
bool Make::specifyProperty(const String &name, const String &value)
{
    if (specifiedProperties.find(name) != specifiedProperties.end())
        {
        error("Property %s already specified", name.c_str());
        return false;
        }
    specifiedProperties[name] = value;
    return true;
}



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

    return true;
}




/**
 * Get a formatted MM:SS.sss time elapsed string
 */ 
static String
timeDiffString(struct timeval &x, struct timeval &y)
{
    long microsX  = x.tv_usec;
    long secondsX = x.tv_sec;
    long microsY  = y.tv_usec;
    long secondsY = y.tv_sec;
    if (microsX < microsY)
        {
        microsX += 1000000;
        secondsX -= 1;
        }

    int seconds = (int)(secondsX - secondsY);
    int millis  = (int)((microsX - microsY)/1000);

    int minutes = seconds/60;
    seconds -= minutes*60;
    char buf[80];
    snprintf(buf, 79, "%dm %d.%03ds", minutes, seconds, millis);
    String ret = buf;
    return ret;
    
}

/**
 *
 */
bool Make::run(const String &target)
{
    status("####################################################");
    status("#   %s", version().c_str());
    status("####################################################");
    struct timeval timeStart, timeEnd;
    ::gettimeofday(&timeStart, NULL);
    specifiedTarget = target;
    if (!run())
        return false;
    ::gettimeofday(&timeEnd, NULL);
    String timeStr = timeDiffString(timeEnd, timeStart);
    status("####################################################");
    status("#   BuildTool Completed : %s", timeStr.c_str());
    status("####################################################");
    return true;
}







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

typedef buildtool::String String;

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


static bool parseProperty(const String &s, String &name, String &val)
{
    int len = s.size();
    int i;
    for (i=0 ; i<len ; i++)
        {
        char ch = s[i];
        if (ch == '=')
            break;
        name.push_back(ch);
        }
    if (i>=len || s[i]!='=')
        {
        error("property requires -Dname=value");
        return false;
        }
    i++;
    for ( ; i<len ; i++)
        {
        char ch = s[i];
        val.push_back(ch);
        }
    return true;
}


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

static void usage(int argc, char **argv)
{
    printf("usage:\n");
    printf("   %s [options] [target]\n", argv[0]);
    printf("Options:\n");
    printf("  -help, -h              print this message\n");
    printf("  -version               print the version information and exit\n");
    printf("  -file <file>           use given buildfile\n");
    printf("  -f <file>                 ''\n");
    printf("  -D<property>=<value>   use value for given property\n");
    printf("  -j [N]                 build using N threads or infinite number of threads if no argument\n");
}




/**
 * 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::Make make;

    String target;

    //char *progName = argv[0];
    for (int i=1 ; i<argc ; i++)
        {
        String arg = argv[i];
        if (arg.size()>1 && arg[0]=='-')
            {
            if (arg == "-h" || arg == "-help")
                {
                usage(argc,argv);
                return true;
                }
            else if (arg == "-version")
                {
                printf("%s", make.version().c_str());
                return true;
                }
            else if (arg == "-f" || arg == "-file")
                {
                if (i>=argc-1)
                   {
                   usage(argc, argv);
                   return false;
                   }
                i++; //eat option
                make.setURI(argv[i]);
                }
            else if (arg == "-j")
            {
                if (i>=argc-1) {  // if -j is given as last argument
                    make.setNumThreads(20); // default to some high value
                } else {
                    i++; //eat option
                    if (argv[i] && (*argv[i] == '-')) { // if -j is followed by another '-...' option
                        make.setNumThreads(20); // default to some high value
                    } else {
                        make.setNumThreads(atoi(argv[i]));
                    }
                }
            }
            else if (arg.size()>2 && sequ(arg, "-D"))
                {
                String s = arg.substr(2, arg.size());
                String name, value;
                if (!parseProperty(s, name, value))
                   {
                   usage(argc, argv);
                   return false;
                   }
                if (!make.specifyProperty(name, value))
                    return false;
                }
            else
                {
                error("Unknown option:%s", arg.c_str());
                return false;
                }
            }
        else
            {
            if (target.size()>0)
                {
                error("only one initial target");
                usage(argc, argv);
                return false;
                }
            target = arg;
            }
        }

    //We have the options.  Now execute them
    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<buildtool::FileRec> 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 
//########################################################################


