Code

fix result piping from shelled-out commands on win32
[inkscape.git] / buildtool.cpp
1 /**
2  * Simple build automation tool.
3  *
4  * Authors:
5  *   Bob Jamison
6  *
7  * Copyright (C) 2006-2007 Bob Jamison
8  *
9  *  This library is free software; you can redistribute it and/or
10  *  modify it under the terms of the GNU Lesser General Public
11  *  License as published by the Free Software Foundation; either
12  *  version 2.1 of the License, or (at your option) any later version.
13  *
14  *  This library is distributed in the hope that it will be useful,
15  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  *  Lesser General Public License for more details.
18  *
19  *  You should have received a copy of the GNU Lesser General Public
20  *  License along with this library; if not, write to the Free Software
21  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
22  */
24 /**
25  * To use this file, compile with:
26  * <pre>
27  * g++ -O3 buildtool.cpp -o btool.exe
28  * (or whatever your compiler might be) 
29  * Then
30  * btool
31  * or 
32  * btool {target}
33  * 
34  * Note: recent win32api builds from MinGW have gettimeofday()
35  * defined, so you might need to build with 
36  * g++ -O3 -DHAVE_GETTIMEOFDAY buildtool.cpp -o btool.exe
37  *     
38  */  
40 #define BUILDTOOL_VERSION  "BuildTool v0.6.2, 2007 Bob Jamison"
42 #include <stdio.h>
43 #include <unistd.h>
44 #include <stdarg.h>
45 #include <sys/stat.h>
46 #include <time.h>
47 #include <sys/time.h>
48 #include <dirent.h>
50 #include <string>
51 #include <map>
52 #include <set>
53 #include <vector>
55 #ifdef __WIN32__
56 #include <windows.h>
57 #endif
60 #include <errno.h>
63 //########################################################################
64 //# Definition of gettimeofday() for those who don't have it
65 //########################################################################
66 #ifndef HAVE_GETTIMEOFDAY
67 #include <sys/timeb.h>
69 struct timezone {
70       int tz_minuteswest; /* minutes west of Greenwich */
71       int tz_dsttime;     /* type of dst correction */
72     };
74 static int gettimeofday (struct timeval *tv, struct timezone *tz)
75 {
76    struct _timeb tb;
78    if (!tv)
79       return (-1);
81     _ftime (&tb);
82     tv->tv_sec  = tb.time;
83     tv->tv_usec = tb.millitm * 1000 + 500;
84     if (tz)
85         {
86         tz->tz_minuteswest = -60 * _timezone;
87         tz->tz_dsttime = _daylight;
88         }
89     return 0;
90 }
92 #endif
100 namespace buildtool
106 //########################################################################
107 //########################################################################
108 //##  R E G E X P
109 //########################################################################
110 //########################################################################
112 /**
113  * This is the T-Rex regular expression library, which we
114  * gratefully acknowledge.  It's clean code and small size allow
115  * us to embed it in BuildTool without adding a dependency
116  *
117  */    
119 //begin trex.h
121 #ifndef _TREX_H_
122 #define _TREX_H_
123 /***************************************************************
124     T-Rex a tiny regular expression library
126     Copyright (C) 2003-2006 Alberto Demichelis
128     This software is provided 'as-is', without any express 
129     or implied warranty. In no event will the authors be held 
130     liable for any damages arising from the use of this software.
132     Permission is granted to anyone to use this software for 
133     any purpose, including commercial applications, and to alter
134     it and redistribute it freely, subject to the following restrictions:
136         1. The origin of this software must not be misrepresented;
137         you must not claim that you wrote the original software.
138         If you use this software in a product, an acknowledgment
139         in the product documentation would be appreciated but
140         is not required.
142         2. Altered source versions must be plainly marked as such,
143         and must not be misrepresented as being the original software.
145         3. This notice may not be removed or altered from any
146         source distribution.
148 ****************************************************************/
150 #ifdef _UNICODE
151 #define TRexChar unsigned short
152 #define MAX_CHAR 0xFFFF
153 #define _TREXC(c) L##c 
154 #define trex_strlen wcslen
155 #define trex_printf wprintf
156 #else
157 #define TRexChar char
158 #define MAX_CHAR 0xFF
159 #define _TREXC(c) (c) 
160 #define trex_strlen strlen
161 #define trex_printf printf
162 #endif
164 #ifndef TREX_API
165 #define TREX_API extern
166 #endif
168 #define TRex_True 1
169 #define TRex_False 0
171 typedef unsigned int TRexBool;
172 typedef struct TRex TRex;
174 typedef struct {
175     const TRexChar *begin;
176     int len;
177 } TRexMatch;
179 TREX_API TRex *trex_compile(const TRexChar *pattern,const TRexChar **error);
180 TREX_API void trex_free(TRex *exp);
181 TREX_API TRexBool trex_match(TRex* exp,const TRexChar* text);
182 TREX_API TRexBool trex_search(TRex* exp,const TRexChar* text, const TRexChar** out_begin, const TRexChar** out_end);
183 TREX_API TRexBool trex_searchrange(TRex* exp,const TRexChar* text_begin,const TRexChar* text_end,const TRexChar** out_begin, const TRexChar** out_end);
184 TREX_API int trex_getsubexpcount(TRex* exp);
185 TREX_API TRexBool trex_getsubexp(TRex* exp, int n, TRexMatch *subexp);
187 #endif
189 //end trex.h
191 //start trex.c
194 #include <stdio.h>
195 #include <string>
197 /* see copyright notice in trex.h */
198 #include <string.h>
199 #include <stdlib.h>
200 #include <ctype.h>
201 #include <setjmp.h>
202 //#include "trex.h"
204 #ifdef _UINCODE
205 #define scisprint iswprint
206 #define scstrlen wcslen
207 #define scprintf wprintf
208 #define _SC(x) L(x)
209 #else
210 #define scisprint isprint
211 #define scstrlen strlen
212 #define scprintf printf
213 #define _SC(x) (x)
214 #endif
216 #ifdef _DEBUG
217 #include <stdio.h>
219 static const TRexChar *g_nnames[] =
221     _SC("NONE"),_SC("OP_GREEDY"),    _SC("OP_OR"),
222     _SC("OP_EXPR"),_SC("OP_NOCAPEXPR"),_SC("OP_DOT"),    _SC("OP_CLASS"),
223     _SC("OP_CCLASS"),_SC("OP_NCLASS"),_SC("OP_RANGE"),_SC("OP_CHAR"),
224     _SC("OP_EOL"),_SC("OP_BOL"),_SC("OP_WB")
225 };
227 #endif
228 #define OP_GREEDY        (MAX_CHAR+1) // * + ? {n}
229 #define OP_OR            (MAX_CHAR+2)
230 #define OP_EXPR            (MAX_CHAR+3) //parentesis ()
231 #define OP_NOCAPEXPR    (MAX_CHAR+4) //parentesis (?:)
232 #define OP_DOT            (MAX_CHAR+5)
233 #define OP_CLASS        (MAX_CHAR+6)
234 #define OP_CCLASS        (MAX_CHAR+7)
235 #define OP_NCLASS        (MAX_CHAR+8) //negates class the [^
236 #define OP_RANGE        (MAX_CHAR+9)
237 #define OP_CHAR            (MAX_CHAR+10)
238 #define OP_EOL            (MAX_CHAR+11)
239 #define OP_BOL            (MAX_CHAR+12)
240 #define OP_WB            (MAX_CHAR+13)
242 #define TREX_SYMBOL_ANY_CHAR ('.')
243 #define TREX_SYMBOL_GREEDY_ONE_OR_MORE ('+')
244 #define TREX_SYMBOL_GREEDY_ZERO_OR_MORE ('*')
245 #define TREX_SYMBOL_GREEDY_ZERO_OR_ONE ('?')
246 #define TREX_SYMBOL_BRANCH ('|')
247 #define TREX_SYMBOL_END_OF_STRING ('$')
248 #define TREX_SYMBOL_BEGINNING_OF_STRING ('^')
249 #define TREX_SYMBOL_ESCAPE_CHAR ('\\')
252 typedef int TRexNodeType;
254 typedef struct tagTRexNode{
255     TRexNodeType type;
256     int left;
257     int right;
258     int next;
259 }TRexNode;
261 struct TRex{
262     const TRexChar *_eol;
263     const TRexChar *_bol;
264     const TRexChar *_p;
265     int _first;
266     int _op;
267     TRexNode *_nodes;
268     int _nallocated;
269     int _nsize;
270     int _nsubexpr;
271     TRexMatch *_matches;
272     int _currsubexp;
273     void *_jmpbuf;
274     const TRexChar **_error;
275 };
277 static int trex_list(TRex *exp);
279 static int trex_newnode(TRex *exp, TRexNodeType type)
281     TRexNode n;
282     int newid;
283     n.type = type;
284     n.next = n.right = n.left = -1;
285     if(type == OP_EXPR)
286         n.right = exp->_nsubexpr++;
287     if(exp->_nallocated < (exp->_nsize + 1)) {
288         //int oldsize = exp->_nallocated;
289         exp->_nallocated *= 2;
290         exp->_nodes = (TRexNode *)realloc(exp->_nodes, exp->_nallocated * sizeof(TRexNode));
291     }
292     exp->_nodes[exp->_nsize++] = n;
293     newid = exp->_nsize - 1;
294     return (int)newid;
297 static void trex_error(TRex *exp,const TRexChar *error)
299     if(exp->_error) *exp->_error = error;
300     longjmp(*((jmp_buf*)exp->_jmpbuf),-1);
303 static void trex_expect(TRex *exp, int n){
304     if((*exp->_p) != n) 
305         trex_error(exp, _SC("expected paren"));
306     exp->_p++;
309 static TRexChar trex_escapechar(TRex *exp)
311     if(*exp->_p == TREX_SYMBOL_ESCAPE_CHAR){
312         exp->_p++;
313         switch(*exp->_p) {
314         case 'v': exp->_p++; return '\v';
315         case 'n': exp->_p++; return '\n';
316         case 't': exp->_p++; return '\t';
317         case 'r': exp->_p++; return '\r';
318         case 'f': exp->_p++; return '\f';
319         default: return (*exp->_p++);
320         }
321     } else if(!scisprint(*exp->_p)) trex_error(exp,_SC("letter expected"));
322     return (*exp->_p++);
325 static int trex_charclass(TRex *exp,int classid)
327     int n = trex_newnode(exp,OP_CCLASS);
328     exp->_nodes[n].left = classid;
329     return n;
332 static int trex_charnode(TRex *exp,TRexBool isclass)
334     TRexChar t;
335     if(*exp->_p == TREX_SYMBOL_ESCAPE_CHAR) {
336         exp->_p++;
337         switch(*exp->_p) {
338             case 'n': exp->_p++; return trex_newnode(exp,'\n');
339             case 't': exp->_p++; return trex_newnode(exp,'\t');
340             case 'r': exp->_p++; return trex_newnode(exp,'\r');
341             case 'f': exp->_p++; return trex_newnode(exp,'\f');
342             case 'v': exp->_p++; return trex_newnode(exp,'\v');
343             case 'a': case 'A': case 'w': case 'W': case 's': case 'S': 
344             case 'd': case 'D': case 'x': case 'X': case 'c': case 'C': 
345             case 'p': case 'P': case 'l': case 'u': 
346                 {
347                 t = *exp->_p; exp->_p++; 
348                 return trex_charclass(exp,t);
349                 }
350             case 'b': 
351             case 'B':
352                 if(!isclass) {
353                     int node = trex_newnode(exp,OP_WB);
354                     exp->_nodes[node].left = *exp->_p;
355                     exp->_p++; 
356                     return node;
357                 } //else default
358             default: 
359                 t = *exp->_p; exp->_p++; 
360                 return trex_newnode(exp,t);
361         }
362     }
363     else if(!scisprint(*exp->_p)) {
364         
365         trex_error(exp,_SC("letter expected"));
366     }
367     t = *exp->_p; exp->_p++; 
368     return trex_newnode(exp,t);
370 static int trex_class(TRex *exp)
372     int ret = -1;
373     int first = -1,chain;
374     if(*exp->_p == TREX_SYMBOL_BEGINNING_OF_STRING){
375         ret = trex_newnode(exp,OP_NCLASS);
376         exp->_p++;
377     }else ret = trex_newnode(exp,OP_CLASS);
378     
379     if(*exp->_p == ']') trex_error(exp,_SC("empty class"));
380     chain = ret;
381     while(*exp->_p != ']' && exp->_p != exp->_eol) {
382         if(*exp->_p == '-' && first != -1){ 
383             int r,t;
384             if(*exp->_p++ == ']') trex_error(exp,_SC("unfinished range"));
385             r = trex_newnode(exp,OP_RANGE);
386             if(first>*exp->_p) trex_error(exp,_SC("invalid range"));
387             if(exp->_nodes[first].type == OP_CCLASS) trex_error(exp,_SC("cannot use character classes in ranges"));
388             exp->_nodes[r].left = exp->_nodes[first].type;
389             t = trex_escapechar(exp);
390             exp->_nodes[r].right = t;
391             exp->_nodes[chain].next = r;
392             chain = r;
393             first = -1;
394         }
395         else{
396             if(first!=-1){
397                 int c = first;
398                 exp->_nodes[chain].next = c;
399                 chain = c;
400                 first = trex_charnode(exp,TRex_True);
401             }
402             else{
403                 first = trex_charnode(exp,TRex_True);
404             }
405         }
406     }
407     if(first!=-1){
408         int c = first;
409         exp->_nodes[chain].next = c;
410         chain = c;
411         first = -1;
412     }
413     /* hack? */
414     exp->_nodes[ret].left = exp->_nodes[ret].next;
415     exp->_nodes[ret].next = -1;
416     return ret;
419 static int trex_parsenumber(TRex *exp)
421     int ret = *exp->_p-'0';
422     int positions = 10;
423     exp->_p++;
424     while(isdigit(*exp->_p)) {
425         ret = ret*10+(*exp->_p++-'0');
426         if(positions==1000000000) trex_error(exp,_SC("overflow in numeric constant"));
427         positions *= 10;
428     };
429     return ret;
432 static int trex_element(TRex *exp)
434     int ret = -1;
435     switch(*exp->_p)
436     {
437     case '(': {
438         int expr,newn;
439         exp->_p++;
442         if(*exp->_p =='?') {
443             exp->_p++;
444             trex_expect(exp,':');
445             expr = trex_newnode(exp,OP_NOCAPEXPR);
446         }
447         else
448             expr = trex_newnode(exp,OP_EXPR);
449         newn = trex_list(exp);
450         exp->_nodes[expr].left = newn;
451         ret = expr;
452         trex_expect(exp,')');
453               }
454               break;
455     case '[':
456         exp->_p++;
457         ret = trex_class(exp);
458         trex_expect(exp,']');
459         break;
460     case TREX_SYMBOL_END_OF_STRING: exp->_p++; ret = trex_newnode(exp,OP_EOL);break;
461     case TREX_SYMBOL_ANY_CHAR: exp->_p++; ret = trex_newnode(exp,OP_DOT);break;
462     default:
463         ret = trex_charnode(exp,TRex_False);
464         break;
465     }
467     {
468         int op;
469         TRexBool isgreedy = TRex_False;
470         unsigned short p0 = 0, p1 = 0;
471         switch(*exp->_p){
472             case TREX_SYMBOL_GREEDY_ZERO_OR_MORE: p0 = 0; p1 = 0xFFFF; exp->_p++; isgreedy = TRex_True; break;
473             case TREX_SYMBOL_GREEDY_ONE_OR_MORE: p0 = 1; p1 = 0xFFFF; exp->_p++; isgreedy = TRex_True; break;
474             case TREX_SYMBOL_GREEDY_ZERO_OR_ONE: p0 = 0; p1 = 1; exp->_p++; isgreedy = TRex_True; break;
475             case '{':
476                 exp->_p++;
477                 if(!isdigit(*exp->_p)) trex_error(exp,_SC("number expected"));
478                 p0 = (unsigned short)trex_parsenumber(exp);
479                 /*******************************/
480                 switch(*exp->_p) {
481             case '}':
482                 p1 = p0; exp->_p++;
483                 break;
484             case ',':
485                 exp->_p++;
486                 p1 = 0xFFFF;
487                 if(isdigit(*exp->_p)){
488                     p1 = (unsigned short)trex_parsenumber(exp);
489                 }
490                 trex_expect(exp,'}');
491                 break;
492             default:
493                 trex_error(exp,_SC(", or } expected"));
494         }
495         /*******************************/
496         isgreedy = TRex_True; 
497         break;
499         }
500         if(isgreedy) {
501             int nnode = trex_newnode(exp,OP_GREEDY);
502             op = OP_GREEDY;
503             exp->_nodes[nnode].left = ret;
504             exp->_nodes[nnode].right = ((p0)<<16)|p1;
505             ret = nnode;
506         }
507     }
508     if((*exp->_p != TREX_SYMBOL_BRANCH) && (*exp->_p != ')') && (*exp->_p != TREX_SYMBOL_GREEDY_ZERO_OR_MORE) && (*exp->_p != TREX_SYMBOL_GREEDY_ONE_OR_MORE) && (*exp->_p != '\0')) {
509         int nnode = trex_element(exp);
510         exp->_nodes[ret].next = nnode;
511     }
513     return ret;
516 static int trex_list(TRex *exp)
518     int ret=-1,e;
519     if(*exp->_p == TREX_SYMBOL_BEGINNING_OF_STRING) {
520         exp->_p++;
521         ret = trex_newnode(exp,OP_BOL);
522     }
523     e = trex_element(exp);
524     if(ret != -1) {
525         exp->_nodes[ret].next = e;
526     }
527     else ret = e;
529     if(*exp->_p == TREX_SYMBOL_BRANCH) {
530         int temp,tright;
531         exp->_p++;
532         temp = trex_newnode(exp,OP_OR);
533         exp->_nodes[temp].left = ret;
534         tright = trex_list(exp);
535         exp->_nodes[temp].right = tright;
536         ret = temp;
537     }
538     return ret;
541 static TRexBool trex_matchcclass(int cclass,TRexChar c)
543     switch(cclass) {
544     case 'a': return isalpha(c)?TRex_True:TRex_False;
545     case 'A': return !isalpha(c)?TRex_True:TRex_False;
546     case 'w': return (isalnum(c) || c == '_')?TRex_True:TRex_False;
547     case 'W': return (!isalnum(c) && c != '_')?TRex_True:TRex_False;
548     case 's': return isspace(c)?TRex_True:TRex_False;
549     case 'S': return !isspace(c)?TRex_True:TRex_False;
550     case 'd': return isdigit(c)?TRex_True:TRex_False;
551     case 'D': return !isdigit(c)?TRex_True:TRex_False;
552     case 'x': return isxdigit(c)?TRex_True:TRex_False;
553     case 'X': return !isxdigit(c)?TRex_True:TRex_False;
554     case 'c': return iscntrl(c)?TRex_True:TRex_False;
555     case 'C': return !iscntrl(c)?TRex_True:TRex_False;
556     case 'p': return ispunct(c)?TRex_True:TRex_False;
557     case 'P': return !ispunct(c)?TRex_True:TRex_False;
558     case 'l': return islower(c)?TRex_True:TRex_False;
559     case 'u': return isupper(c)?TRex_True:TRex_False;
560     }
561     return TRex_False; /*cannot happen*/
564 static TRexBool trex_matchclass(TRex* exp,TRexNode *node,TRexChar c)
566     do {
567         switch(node->type) {
568             case OP_RANGE:
569                 if(c >= node->left && c <= node->right) return TRex_True;
570                 break;
571             case OP_CCLASS:
572                 if(trex_matchcclass(node->left,c)) return TRex_True;
573                 break;
574             default:
575                 if(c == node->type)return TRex_True;
576         }
577     } while((node->next != -1) && (node = &exp->_nodes[node->next]));
578     return TRex_False;
581 static const TRexChar *trex_matchnode(TRex* exp,TRexNode *node,const TRexChar *str,TRexNode *next)
583     
584     TRexNodeType type = node->type;
585     switch(type) {
586     case OP_GREEDY: {
587         //TRexNode *greedystop = (node->next != -1) ? &exp->_nodes[node->next] : NULL;
588         TRexNode *greedystop = NULL;
589         int p0 = (node->right >> 16)&0x0000FFFF, p1 = node->right&0x0000FFFF, nmaches = 0;
590         const TRexChar *s=str, *good = str;
592         if(node->next != -1) {
593             greedystop = &exp->_nodes[node->next];
594         }
595         else {
596             greedystop = next;
597         }
599         while((nmaches == 0xFFFF || nmaches < p1)) {
601             const TRexChar *stop;
602             if(!(s = trex_matchnode(exp,&exp->_nodes[node->left],s,greedystop)))
603                 break;
604             nmaches++;
605             good=s;
606             if(greedystop) {
607                 //checks that 0 matches satisfy the expression(if so skips)
608                 //if not would always stop(for instance if is a '?')
609                 if(greedystop->type != OP_GREEDY ||
610                 (greedystop->type == OP_GREEDY && ((greedystop->right >> 16)&0x0000FFFF) != 0))
611                 {
612                     TRexNode *gnext = NULL;
613                     if(greedystop->next != -1) {
614                         gnext = &exp->_nodes[greedystop->next];
615                     }else if(next && next->next != -1){
616                         gnext = &exp->_nodes[next->next];
617                     }
618                     stop = trex_matchnode(exp,greedystop,s,gnext);
619                     if(stop) {
620                         //if satisfied stop it
621                         if(p0 == p1 && p0 == nmaches) break;
622                         else if(nmaches >= p0 && p1 == 0xFFFF) break;
623                         else if(nmaches >= p0 && nmaches <= p1) break;
624                     }
625                 }
626             }
627             
628             if(s >= exp->_eol)
629                 break;
630         }
631         if(p0 == p1 && p0 == nmaches) return good;
632         else if(nmaches >= p0 && p1 == 0xFFFF) return good;
633         else if(nmaches >= p0 && nmaches <= p1) return good;
634         return NULL;
635     }
636     case OP_OR: {
637             const TRexChar *asd = str;
638             TRexNode *temp=&exp->_nodes[node->left];
639             while( (asd = trex_matchnode(exp,temp,asd,NULL)) ) {
640                 if(temp->next != -1)
641                     temp = &exp->_nodes[temp->next];
642                 else
643                     return asd;
644             }
645             asd = str;
646             temp = &exp->_nodes[node->right];
647             while( (asd = trex_matchnode(exp,temp,asd,NULL)) ) {
648                 if(temp->next != -1)
649                     temp = &exp->_nodes[temp->next];
650                 else
651                     return asd;
652             }
653             return NULL;
654             break;
655     }
656     case OP_EXPR:
657     case OP_NOCAPEXPR:{
658             TRexNode *n = &exp->_nodes[node->left];
659             const TRexChar *cur = str;
660             int capture = -1;
661             if(node->type != OP_NOCAPEXPR && node->right == exp->_currsubexp) {
662                 capture = exp->_currsubexp;
663                 exp->_matches[capture].begin = cur;
664                 exp->_currsubexp++;
665             }
666             
667             do {
668                 TRexNode *subnext = NULL;
669                 if(n->next != -1) {
670                     subnext = &exp->_nodes[n->next];
671                 }else {
672                     subnext = next;
673                 }
674                 if(!(cur = trex_matchnode(exp,n,cur,subnext))) {
675                     if(capture != -1){
676                         exp->_matches[capture].begin = 0;
677                         exp->_matches[capture].len = 0;
678                     }
679                     return NULL;
680                 }
681             } while((n->next != -1) && (n = &exp->_nodes[n->next]));
683             if(capture != -1) 
684                 exp->_matches[capture].len = cur - exp->_matches[capture].begin;
685             return cur;
686     }                 
687     case OP_WB:
688         if(str == exp->_bol && !isspace(*str)
689          || (str == exp->_eol && !isspace(*(str-1)))
690          || (!isspace(*str) && isspace(*(str+1)))
691          || (isspace(*str) && !isspace(*(str+1))) ) {
692             return (node->left == 'b')?str:NULL;
693         }
694         return (node->left == 'b')?NULL:str;
695     case OP_BOL:
696         if(str == exp->_bol) return str;
697         return NULL;
698     case OP_EOL:
699         if(str == exp->_eol) return str;
700         return NULL;
701     case OP_DOT:{
702         *str++;
703                 }
704         return str;
705     case OP_NCLASS:
706     case OP_CLASS:
707         if(trex_matchclass(exp,&exp->_nodes[node->left],*str)?(type == OP_CLASS?TRex_True:TRex_False):(type == OP_NCLASS?TRex_True:TRex_False)) {
708             *str++;
709             return str;
710         }
711         return NULL;
712     case OP_CCLASS:
713         if(trex_matchcclass(node->left,*str)) {
714             *str++;
715             return str;
716         }
717         return NULL;
718     default: /* char */
719         if(*str != node->type) return NULL;
720         *str++;
721         return str;
722     }
723     return NULL;
726 /* public api */
727 TRex *trex_compile(const TRexChar *pattern,const TRexChar **error)
729     TRex *exp = (TRex *)malloc(sizeof(TRex));
730     exp->_eol = exp->_bol = NULL;
731     exp->_p = pattern;
732     exp->_nallocated = (int)scstrlen(pattern) * sizeof(TRexChar);
733     exp->_nodes = (TRexNode *)malloc(exp->_nallocated * sizeof(TRexNode));
734     exp->_nsize = 0;
735     exp->_matches = 0;
736     exp->_nsubexpr = 0;
737     exp->_first = trex_newnode(exp,OP_EXPR);
738     exp->_error = error;
739     exp->_jmpbuf = malloc(sizeof(jmp_buf));
740     if(setjmp(*((jmp_buf*)exp->_jmpbuf)) == 0) {
741         int res = trex_list(exp);
742         exp->_nodes[exp->_first].left = res;
743         if(*exp->_p!='\0')
744             trex_error(exp,_SC("unexpected character"));
745 #ifdef _DEBUG
746         {
747             int nsize,i;
748             TRexNode *t;
749             nsize = exp->_nsize;
750             t = &exp->_nodes[0];
751             scprintf(_SC("\n"));
752             for(i = 0;i < nsize; i++) {
753                 if(exp->_nodes[i].type>MAX_CHAR)
754                     scprintf(_SC("[%02d] %10s "),i,g_nnames[exp->_nodes[i].type-MAX_CHAR]);
755                 else
756                     scprintf(_SC("[%02d] %10c "),i,exp->_nodes[i].type);
757                 scprintf(_SC("left %02d right %02d next %02d\n"),exp->_nodes[i].left,exp->_nodes[i].right,exp->_nodes[i].next);
758             }
759             scprintf(_SC("\n"));
760         }
761 #endif
762         exp->_matches = (TRexMatch *) malloc(exp->_nsubexpr * sizeof(TRexMatch));
763         memset(exp->_matches,0,exp->_nsubexpr * sizeof(TRexMatch));
764     }
765     else{
766         trex_free(exp);
767         return NULL;
768     }
769     return exp;
772 void trex_free(TRex *exp)
774     if(exp)    {
775         if(exp->_nodes) free(exp->_nodes);
776         if(exp->_jmpbuf) free(exp->_jmpbuf);
777         if(exp->_matches) free(exp->_matches);
778         free(exp);
779     }
782 TRexBool trex_match(TRex* exp,const TRexChar* text)
784     const TRexChar* res = NULL;
785     exp->_bol = text;
786     exp->_eol = text + scstrlen(text);
787     exp->_currsubexp = 0;
788     res = trex_matchnode(exp,exp->_nodes,text,NULL);
789     if(res == NULL || res != exp->_eol)
790         return TRex_False;
791     return TRex_True;
794 TRexBool trex_searchrange(TRex* exp,const TRexChar* text_begin,const TRexChar* text_end,const TRexChar** out_begin, const TRexChar** out_end)
796     const TRexChar *cur = NULL;
797     int node = exp->_first;
798     if(text_begin >= text_end) return TRex_False;
799     exp->_bol = text_begin;
800     exp->_eol = text_end;
801     do {
802         cur = text_begin;
803         while(node != -1) {
804             exp->_currsubexp = 0;
805             cur = trex_matchnode(exp,&exp->_nodes[node],cur,NULL);
806             if(!cur)
807                 break;
808             node = exp->_nodes[node].next;
809         }
810         *text_begin++;
811     } while(cur == NULL && text_begin != text_end);
813     if(cur == NULL)
814         return TRex_False;
816     --text_begin;
818     if(out_begin) *out_begin = text_begin;
819     if(out_end) *out_end = cur;
820     return TRex_True;
823 TRexBool trex_search(TRex* exp,const TRexChar* text, const TRexChar** out_begin, const TRexChar** out_end)
825     return trex_searchrange(exp,text,text + scstrlen(text),out_begin,out_end);
828 int trex_getsubexpcount(TRex* exp)
830     return exp->_nsubexpr;
833 TRexBool trex_getsubexp(TRex* exp, int n, TRexMatch *subexp)
835     if( n<0 || n >= exp->_nsubexpr) return TRex_False;
836     *subexp = exp->_matches[n];
837     return TRex_True;
841 //########################################################################
842 //########################################################################
843 //##  E N D    R E G E X P
844 //########################################################################
845 //########################################################################
851 //########################################################################
852 //########################################################################
853 //##  X M L
854 //########################################################################
855 //########################################################################
857 // Note:  This mini-dom library comes from Pedro, another little project
858 // of mine.
860 typedef std::string String;
861 typedef unsigned int XMLCh;
864 class Namespace
866 public:
867     Namespace()
868         {}
870     Namespace(const String &prefixArg, const String &namespaceURIArg)
871         {
872         prefix       = prefixArg;
873         namespaceURI = namespaceURIArg;
874         }
876     Namespace(const Namespace &other)
877         {
878         assign(other);
879         }
881     Namespace &operator=(const Namespace &other)
882         {
883         assign(other);
884         return *this;
885         }
887     virtual ~Namespace()
888         {}
890     virtual String getPrefix()
891         { return prefix; }
893     virtual String getNamespaceURI()
894         { return namespaceURI; }
896 protected:
898     void assign(const Namespace &other)
899         {
900         prefix       = other.prefix;
901         namespaceURI = other.namespaceURI;
902         }
904     String prefix;
905     String namespaceURI;
907 };
909 class Attribute
911 public:
912     Attribute()
913         {}
915     Attribute(const String &nameArg, const String &valueArg)
916         {
917         name  = nameArg;
918         value = valueArg;
919         }
921     Attribute(const Attribute &other)
922         {
923         assign(other);
924         }
926     Attribute &operator=(const Attribute &other)
927         {
928         assign(other);
929         return *this;
930         }
932     virtual ~Attribute()
933         {}
935     virtual String getName()
936         { return name; }
938     virtual String getValue()
939         { return value; }
941 protected:
943     void assign(const Attribute &other)
944         {
945         name  = other.name;
946         value = other.value;
947         }
949     String name;
950     String value;
952 };
955 class Element
957 friend class Parser;
959 public:
960     Element()
961         {
962         parent = NULL;
963         }
965     Element(const String &nameArg)
966         {
967         parent = NULL;
968         name   = nameArg;
969         }
971     Element(const String &nameArg, const String &valueArg)
972         {
973         parent = NULL;
974         name   = nameArg;
975         value  = valueArg;
976         }
978     Element(const Element &other)
979         {
980         assign(other);
981         }
983     Element &operator=(const Element &other)
984         {
985         assign(other);
986         return *this;
987         }
989     virtual Element *clone();
991     virtual ~Element()
992         {
993         for (unsigned int i=0 ; i<children.size() ; i++)
994             delete children[i];
995         }
997     virtual String getName()
998         { return name; }
1000     virtual String getValue()
1001         { return value; }
1003     Element *getParent()
1004         { return parent; }
1006     std::vector<Element *> getChildren()
1007         { return children; }
1009     std::vector<Element *> findElements(const String &name);
1011     String getAttribute(const String &name);
1013     std::vector<Attribute> &getAttributes()
1014         { return attributes; } 
1016     String getTagAttribute(const String &tagName, const String &attrName);
1018     String getTagValue(const String &tagName);
1020     void addChild(Element *child);
1022     void addAttribute(const String &name, const String &value);
1024     void addNamespace(const String &prefix, const String &namespaceURI);
1027     /**
1028      * Prettyprint an XML tree to an output stream.  Elements are indented
1029      * according to element hierarchy.
1030      * @param f a stream to receive the output
1031      * @param elem the element to output
1032      */
1033     void writeIndented(FILE *f);
1035     /**
1036      * Prettyprint an XML tree to standard output.  This is the equivalent of
1037      * writeIndented(stdout).
1038      * @param elem the element to output
1039      */
1040     void print();
1042 protected:
1044     void assign(const Element &other)
1045         {
1046         parent     = other.parent;
1047         children   = other.children;
1048         attributes = other.attributes;
1049         namespaces = other.namespaces;
1050         name       = other.name;
1051         value      = other.value;
1052         }
1054     void findElementsRecursive(std::vector<Element *>&res, const String &name);
1056     void writeIndentedRecursive(FILE *f, int indent);
1058     Element *parent;
1060     std::vector<Element *>children;
1062     std::vector<Attribute> attributes;
1063     std::vector<Namespace> namespaces;
1065     String name;
1066     String value;
1068 };
1074 class Parser
1076 public:
1077     /**
1078      * Constructor
1079      */
1080     Parser()
1081         { init(); }
1083     virtual ~Parser()
1084         {}
1086     /**
1087      * Parse XML in a char buffer.
1088      * @param buf a character buffer to parse
1089      * @param pos position to start parsing
1090      * @param len number of chars, from pos, to parse.
1091      * @return a pointer to the root of the XML document;
1092      */
1093     Element *parse(const char *buf,int pos,int len);
1095     /**
1096      * Parse XML in a char buffer.
1097      * @param buf a character buffer to parse
1098      * @param pos position to start parsing
1099      * @param len number of chars, from pos, to parse.
1100      * @return a pointer to the root of the XML document;
1101      */
1102     Element *parse(const String &buf);
1104     /**
1105      * Parse a named XML file.  The file is loaded like a data file;
1106      * the original format is not preserved.
1107      * @param fileName the name of the file to read
1108      * @return a pointer to the root of the XML document;
1109      */
1110     Element *parseFile(const String &fileName);
1112     /**
1113      * Utility method to preprocess a string for XML
1114      * output, escaping its entities.
1115      * @param str the string to encode
1116      */
1117     static String encode(const String &str);
1119     /**
1120      *  Removes whitespace from beginning and end of a string
1121      */
1122     String trim(const String &s);
1124 private:
1126     void init()
1127         {
1128         keepGoing       = true;
1129         currentNode     = NULL;
1130         parselen        = 0;
1131         parsebuf        = NULL;
1132         currentPosition = 0;
1133         }
1135     void getLineAndColumn(long pos, long *lineNr, long *colNr);
1137     void error(char *fmt, ...);
1139     int peek(long pos);
1141     int match(long pos, const char *text);
1143     int skipwhite(long p);
1145     int getWord(int p0, String &buf);
1147     int getQuoted(int p0, String &buf, int do_i_parse);
1149     int parseVersion(int p0);
1151     int parseDoctype(int p0);
1153     int parseElement(int p0, Element *par,int depth);
1155     Element *parse(XMLCh *buf,int pos,int len);
1157     bool       keepGoing;
1158     Element    *currentNode;
1159     long       parselen;
1160     XMLCh      *parsebuf;
1161     String  cdatabuf;
1162     long       currentPosition;
1163     int        colNr;
1165 };
1170 //########################################################################
1171 //# E L E M E N T
1172 //########################################################################
1174 Element *Element::clone()
1176     Element *elem = new Element(name, value);
1177     elem->parent     = parent;
1178     elem->attributes = attributes;
1179     elem->namespaces = namespaces;
1181     std::vector<Element *>::iterator iter;
1182     for (iter = children.begin(); iter != children.end() ; iter++)
1183         {
1184         elem->addChild((*iter)->clone());
1185         }
1186     return elem;
1190 void Element::findElementsRecursive(std::vector<Element *>&res, const String &name)
1192     if (getName() == name)
1193         {
1194         res.push_back(this);
1195         }
1196     for (unsigned int i=0; i<children.size() ; i++)
1197         children[i]->findElementsRecursive(res, name);
1200 std::vector<Element *> Element::findElements(const String &name)
1202     std::vector<Element *> res;
1203     findElementsRecursive(res, name);
1204     return res;
1207 String Element::getAttribute(const String &name)
1209     for (unsigned int i=0 ; i<attributes.size() ; i++)
1210         if (attributes[i].getName() ==name)
1211             return attributes[i].getValue();
1212     return "";
1215 String Element::getTagAttribute(const String &tagName, const String &attrName)
1217     std::vector<Element *>elems = findElements(tagName);
1218     if (elems.size() <1)
1219         return "";
1220     String res = elems[0]->getAttribute(attrName);
1221     return res;
1224 String Element::getTagValue(const String &tagName)
1226     std::vector<Element *>elems = findElements(tagName);
1227     if (elems.size() <1)
1228         return "";
1229     String res = elems[0]->getValue();
1230     return res;
1233 void Element::addChild(Element *child)
1235     if (!child)
1236         return;
1237     child->parent = this;
1238     children.push_back(child);
1242 void Element::addAttribute(const String &name, const String &value)
1244     Attribute attr(name, value);
1245     attributes.push_back(attr);
1248 void Element::addNamespace(const String &prefix, const String &namespaceURI)
1250     Namespace ns(prefix, namespaceURI);
1251     namespaces.push_back(ns);
1254 void Element::writeIndentedRecursive(FILE *f, int indent)
1256     int i;
1257     if (!f)
1258         return;
1259     //Opening tag, and attributes
1260     for (i=0;i<indent;i++)
1261         fputc(' ',f);
1262     fprintf(f,"<%s",name.c_str());
1263     for (unsigned int i=0 ; i<attributes.size() ; i++)
1264         {
1265         fprintf(f," %s=\"%s\"",
1266               attributes[i].getName().c_str(),
1267               attributes[i].getValue().c_str());
1268         }
1269     for (unsigned int i=0 ; i<namespaces.size() ; i++)
1270         {
1271         fprintf(f," xmlns:%s=\"%s\"",
1272               namespaces[i].getPrefix().c_str(),
1273               namespaces[i].getNamespaceURI().c_str());
1274         }
1275     fprintf(f,">\n");
1277     //Between the tags
1278     if (value.size() > 0)
1279         {
1280         for (int i=0;i<indent;i++)
1281             fputc(' ', f);
1282         fprintf(f," %s\n", value.c_str());
1283         }
1285     for (unsigned int i=0 ; i<children.size() ; i++)
1286         children[i]->writeIndentedRecursive(f, indent+2);
1288     //Closing tag
1289     for (int i=0; i<indent; i++)
1290         fputc(' ',f);
1291     fprintf(f,"</%s>\n", name.c_str());
1294 void Element::writeIndented(FILE *f)
1296     writeIndentedRecursive(f, 0);
1299 void Element::print()
1301     writeIndented(stdout);
1305 //########################################################################
1306 //# P A R S E R
1307 //########################################################################
1311 typedef struct
1312     {
1313     char *escaped;
1314     char value;
1315     } EntityEntry;
1317 static EntityEntry entities[] =
1319     { "&amp;" , '&'  },
1320     { "&lt;"  , '<'  },
1321     { "&gt;"  , '>'  },
1322     { "&apos;", '\'' },
1323     { "&quot;", '"'  },
1324     { NULL    , '\0' }
1325 };
1329 /**
1330  *  Removes whitespace from beginning and end of a string
1331  */
1332 String Parser::trim(const String &s)
1334     if (s.size() < 1)
1335         return s;
1336     
1337     //Find first non-ws char
1338     unsigned int begin = 0;
1339     for ( ; begin < s.size() ; begin++)
1340         {
1341         if (!isspace(s[begin]))
1342             break;
1343         }
1345     //Find first non-ws char, going in reverse
1346     unsigned int end = s.size() - 1;
1347     for ( ; end > begin ; end--)
1348         {
1349         if (!isspace(s[end]))
1350             break;
1351         }
1352     //trace("begin:%d  end:%d", begin, end);
1354     String res = s.substr(begin, end-begin+1);
1355     return res;
1358 void Parser::getLineAndColumn(long pos, long *lineNr, long *colNr)
1360     long line = 1;
1361     long col  = 1;
1362     for (long i=0 ; i<pos ; i++)
1363         {
1364         XMLCh ch = parsebuf[i];
1365         if (ch == '\n' || ch == '\r')
1366             {
1367             col = 0;
1368             line ++;
1369             }
1370         else
1371             col++;
1372         }
1373     *lineNr = line;
1374     *colNr  = col;
1379 void Parser::error(char *fmt, ...)
1381     long lineNr;
1382     long colNr;
1383     getLineAndColumn(currentPosition, &lineNr, &colNr);
1384     va_list args;
1385     fprintf(stderr, "xml error at line %ld, column %ld:", lineNr, colNr);
1386     va_start(args,fmt);
1387     vfprintf(stderr,fmt,args);
1388     va_end(args) ;
1389     fprintf(stderr, "\n");
1394 int Parser::peek(long pos)
1396     if (pos >= parselen)
1397         return -1;
1398     currentPosition = pos;
1399     int ch = parsebuf[pos];
1400     //printf("ch:%c\n", ch);
1401     return ch;
1406 String Parser::encode(const String &str)
1408     String ret;
1409     for (unsigned int i=0 ; i<str.size() ; i++)
1410         {
1411         XMLCh ch = (XMLCh)str[i];
1412         if (ch == '&')
1413             ret.append("&amp;");
1414         else if (ch == '<')
1415             ret.append("&lt;");
1416         else if (ch == '>')
1417             ret.append("&gt;");
1418         else if (ch == '\'')
1419             ret.append("&apos;");
1420         else if (ch == '"')
1421             ret.append("&quot;");
1422         else
1423             ret.push_back(ch);
1425         }
1426     return ret;
1430 int Parser::match(long p0, const char *text)
1432     int p = p0;
1433     while (*text)
1434         {
1435         if (peek(p) != *text)
1436             return p0;
1437         p++; text++;
1438         }
1439     return p;
1444 int Parser::skipwhite(long p)
1447     while (p<parselen)
1448         {
1449         int p2 = match(p, "<!--");
1450         if (p2 > p)
1451             {
1452             p = p2;
1453             while (p<parselen)
1454               {
1455               p2 = match(p, "-->");
1456               if (p2 > p)
1457                   {
1458                   p = p2;
1459                   break;
1460                   }
1461               p++;
1462               }
1463           }
1464       XMLCh b = peek(p);
1465       if (!isspace(b))
1466           break;
1467       p++;
1468       }
1469   return p;
1472 /* modify this to allow all chars for an element or attribute name*/
1473 int Parser::getWord(int p0, String &buf)
1475     int p = p0;
1476     while (p<parselen)
1477         {
1478         XMLCh b = peek(p);
1479         if (b<=' ' || b=='/' || b=='>' || b=='=')
1480             break;
1481         buf.push_back(b);
1482         p++;
1483         }
1484     return p;
1487 int Parser::getQuoted(int p0, String &buf, int do_i_parse)
1490     int p = p0;
1491     if (peek(p) != '"' && peek(p) != '\'')
1492         return p0;
1493     p++;
1495     while ( p<parselen )
1496         {
1497         XMLCh b = peek(p);
1498         if (b=='"' || b=='\'')
1499             break;
1500         if (b=='&' && do_i_parse)
1501             {
1502             bool found = false;
1503             for (EntityEntry *ee = entities ; ee->value ; ee++)
1504                 {
1505                 int p2 = match(p, ee->escaped);
1506                 if (p2>p)
1507                     {
1508                     buf.push_back(ee->value);
1509                     p = p2;
1510                     found = true;
1511                     break;
1512                     }
1513                 }
1514             if (!found)
1515                 {
1516                 error("unterminated entity");
1517                 return false;
1518                 }
1519             }
1520         else
1521             {
1522             buf.push_back(b);
1523             p++;
1524             }
1525         }
1526     return p;
1529 int Parser::parseVersion(int p0)
1531     //printf("### parseVersion: %d\n", p0);
1533     int p = p0;
1535     p = skipwhite(p0);
1537     if (peek(p) != '<')
1538         return p0;
1540     p++;
1541     if (p>=parselen || peek(p)!='?')
1542         return p0;
1544     p++;
1546     String buf;
1548     while (p<parselen)
1549         {
1550         XMLCh ch = peek(p);
1551         if (ch=='?')
1552             {
1553             p++;
1554             break;
1555             }
1556         buf.push_back(ch);
1557         p++;
1558         }
1560     if (peek(p) != '>')
1561         return p0;
1562     p++;
1564     //printf("Got version:%s\n",buf.c_str());
1565     return p;
1568 int Parser::parseDoctype(int p0)
1570     //printf("### parseDoctype: %d\n", p0);
1572     int p = p0;
1573     p = skipwhite(p);
1575     if (p>=parselen || peek(p)!='<')
1576         return p0;
1578     p++;
1580     if (peek(p)!='!' || peek(p+1)=='-')
1581         return p0;
1582     p++;
1584     String buf;
1585     while (p<parselen)
1586         {
1587         XMLCh ch = peek(p);
1588         if (ch=='>')
1589             {
1590             p++;
1591             break;
1592             }
1593         buf.push_back(ch);
1594         p++;
1595         }
1597     //printf("Got doctype:%s\n",buf.c_str());
1598     return p;
1601 int Parser::parseElement(int p0, Element *par,int depth)
1604     int p = p0;
1606     int p2 = p;
1608     p = skipwhite(p);
1610     //## Get open tag
1611     XMLCh ch = peek(p);
1612     if (ch!='<')
1613         return p0;
1615     p++;
1617     String openTagName;
1618     p = skipwhite(p);
1619     p = getWord(p, openTagName);
1620     //printf("####tag :%s\n", openTagName.c_str());
1621     p = skipwhite(p);
1623     //Add element to tree
1624     Element *n = new Element(openTagName);
1625     n->parent = par;
1626     par->addChild(n);
1628     // Get attributes
1629     if (peek(p) != '>')
1630         {
1631         while (p<parselen)
1632             {
1633             p = skipwhite(p);
1634             ch = peek(p);
1635             //printf("ch:%c\n",ch);
1636             if (ch=='>')
1637                 break;
1638             else if (ch=='/' && p<parselen+1)
1639                 {
1640                 p++;
1641                 p = skipwhite(p);
1642                 ch = peek(p);
1643                 if (ch=='>')
1644                     {
1645                     p++;
1646                     //printf("quick close\n");
1647                     return p;
1648                     }
1649                 }
1650             String attrName;
1651             p2 = getWord(p, attrName);
1652             if (p2==p)
1653                 break;
1654             //printf("name:%s",buf);
1655             p=p2;
1656             p = skipwhite(p);
1657             ch = peek(p);
1658             //printf("ch:%c\n",ch);
1659             if (ch!='=')
1660                 break;
1661             p++;
1662             p = skipwhite(p);
1663             // ch = parsebuf[p];
1664             // printf("ch:%c\n",ch);
1665             String attrVal;
1666             p2 = getQuoted(p, attrVal, true);
1667             p=p2+1;
1668             //printf("name:'%s'   value:'%s'\n",attrName.c_str(),attrVal.c_str());
1669             char *namestr = (char *)attrName.c_str();
1670             if (strncmp(namestr, "xmlns:", 6)==0)
1671                 n->addNamespace(attrName, attrVal);
1672             else
1673                 n->addAttribute(attrName, attrVal);
1674             }
1675         }
1677     bool cdata = false;
1679     p++;
1680     // ### Get intervening data ### */
1681     String data;
1682     while (p<parselen)
1683         {
1684         //# COMMENT
1685         p2 = match(p, "<!--");
1686         if (!cdata && p2>p)
1687             {
1688             p = p2;
1689             while (p<parselen)
1690                 {
1691                 p2 = match(p, "-->");
1692                 if (p2 > p)
1693                     {
1694                     p = p2;
1695                     break;
1696                     }
1697                 p++;
1698                 }
1699             }
1701         ch = peek(p);
1702         //# END TAG
1703         if (ch=='<' && !cdata && peek(p+1)=='/')
1704             {
1705             break;
1706             }
1707         //# CDATA
1708         p2 = match(p, "<![CDATA[");
1709         if (p2 > p)
1710             {
1711             cdata = true;
1712             p = p2;
1713             continue;
1714             }
1716         //# CHILD ELEMENT
1717         if (ch == '<')
1718             {
1719             p2 = parseElement(p, n, depth+1);
1720             if (p2 == p)
1721                 {
1722                 /*
1723                 printf("problem on element:%s.  p2:%d p:%d\n",
1724                       openTagName.c_str(), p2, p);
1725                 */
1726                 return p0;
1727                 }
1728             p = p2;
1729             continue;
1730             }
1731         //# ENTITY
1732         if (ch=='&' && !cdata)
1733             {
1734             bool found = false;
1735             for (EntityEntry *ee = entities ; ee->value ; ee++)
1736                 {
1737                 int p2 = match(p, ee->escaped);
1738                 if (p2>p)
1739                     {
1740                     data.push_back(ee->value);
1741                     p = p2;
1742                     found = true;
1743                     break;
1744                     }
1745                 }
1746             if (!found)
1747                 {
1748                 error("unterminated entity");
1749                 return -1;
1750                 }
1751             continue;
1752             }
1754         //# NONE OF THE ABOVE
1755         data.push_back(ch);
1756         p++;
1757         }/*while*/
1760     n->value = data;
1761     //printf("%d : data:%s\n",p,data.c_str());
1763     //## Get close tag
1764     p = skipwhite(p);
1765     ch = peek(p);
1766     if (ch != '<')
1767         {
1768         error("no < for end tag\n");
1769         return p0;
1770         }
1771     p++;
1772     ch = peek(p);
1773     if (ch != '/')
1774         {
1775         error("no / on end tag");
1776         return p0;
1777         }
1778     p++;
1779     ch = peek(p);
1780     p = skipwhite(p);
1781     String closeTagName;
1782     p = getWord(p, closeTagName);
1783     if (openTagName != closeTagName)
1784         {
1785         error("Mismatched closing tag.  Expected </%S>. Got '%S'.",
1786                 openTagName.c_str(), closeTagName.c_str());
1787         return p0;
1788         }
1789     p = skipwhite(p);
1790     if (peek(p) != '>')
1791         {
1792         error("no > on end tag for '%s'", closeTagName.c_str());
1793         return p0;
1794         }
1795     p++;
1796     // printf("close element:%s\n",closeTagName.c_str());
1797     p = skipwhite(p);
1798     return p;
1804 Element *Parser::parse(XMLCh *buf,int pos,int len)
1806     parselen = len;
1807     parsebuf = buf;
1808     Element *rootNode = new Element("root");
1809     pos = parseVersion(pos);
1810     pos = parseDoctype(pos);
1811     pos = parseElement(pos, rootNode, 0);
1812     return rootNode;
1816 Element *Parser::parse(const char *buf, int pos, int len)
1818     XMLCh *charbuf = new XMLCh[len + 1];
1819     long i = 0;
1820     for ( ; i < len ; i++)
1821         charbuf[i] = (XMLCh)buf[i];
1822     charbuf[i] = '\0';
1824     Element *n = parse(charbuf, pos, len);
1825     delete[] charbuf;
1826     return n;
1829 Element *Parser::parse(const String &buf)
1831     long len = (long)buf.size();
1832     XMLCh *charbuf = new XMLCh[len + 1];
1833     long i = 0;
1834     for ( ; i < len ; i++)
1835         charbuf[i] = (XMLCh)buf[i];
1836     charbuf[i] = '\0';
1838     Element *n = parse(charbuf, 0, len);
1839     delete[] charbuf;
1840     return n;
1843 Element *Parser::parseFile(const String &fileName)
1846     //##### LOAD INTO A CHAR BUF, THEN CONVERT TO XMLCh
1847     FILE *f = fopen(fileName.c_str(), "rb");
1848     if (!f)
1849         return NULL;
1851     struct stat  statBuf;
1852     if (fstat(fileno(f),&statBuf)<0)
1853         {
1854         fclose(f);
1855         return NULL;
1856         }
1857     long filelen = statBuf.st_size;
1859     //printf("length:%d\n",filelen);
1860     XMLCh *charbuf = new XMLCh[filelen + 1];
1861     for (XMLCh *p=charbuf ; !feof(f) ; p++)
1862         {
1863         *p = (XMLCh)fgetc(f);
1864         }
1865     fclose(f);
1866     charbuf[filelen] = '\0';
1869     /*
1870     printf("nrbytes:%d\n",wc_count);
1871     printf("buf:%ls\n======\n",charbuf);
1872     */
1873     Element *n = parse(charbuf, 0, filelen);
1874     delete[] charbuf;
1875     return n;
1880 //########################################################################
1881 //########################################################################
1882 //##  E N D    X M L
1883 //########################################################################
1884 //########################################################################
1887 //########################################################################
1888 //########################################################################
1889 //##  U R I
1890 //########################################################################
1891 //########################################################################
1893 //This would normally be a call to a UNICODE function
1894 #define isLetter(x) isalpha(x)
1896 /**
1897  *  A class that implements the W3C URI resource reference.
1898  */
1899 class URI
1901 public:
1903     typedef enum
1904         {
1905         SCHEME_NONE =0,
1906         SCHEME_DATA,
1907         SCHEME_HTTP,
1908         SCHEME_HTTPS,
1909         SCHEME_FTP,
1910         SCHEME_FILE,
1911         SCHEME_LDAP,
1912         SCHEME_MAILTO,
1913         SCHEME_NEWS,
1914         SCHEME_TELNET
1915         } SchemeTypes;
1917     /**
1918      *
1919      */
1920     URI()
1921         {
1922         init();
1923         }
1925     /**
1926      *
1927      */
1928     URI(const String &str)
1929         {
1930         init();
1931         parse(str);
1932         }
1935     /**
1936      *
1937      */
1938     URI(const char *str)
1939         {
1940         init();
1941         String domStr = str;
1942         parse(domStr);
1943         }
1946     /**
1947      *
1948      */
1949     URI(const URI &other)
1950         {
1951         init();
1952         assign(other);
1953         }
1956     /**
1957      *
1958      */
1959     URI &operator=(const URI &other)
1960         {
1961         init();
1962         assign(other);
1963         return *this;
1964         }
1967     /**
1968      *
1969      */
1970     virtual ~URI()
1971         {}
1975     /**
1976      *
1977      */
1978     virtual bool parse(const String &str);
1980     /**
1981      *
1982      */
1983     virtual String toString() const;
1985     /**
1986      *
1987      */
1988     virtual int getScheme() const;
1990     /**
1991      *
1992      */
1993     virtual String getSchemeStr() const;
1995     /**
1996      *
1997      */
1998     virtual String getAuthority() const;
2000     /**
2001      *  Same as getAuthority, but if the port has been specified
2002      *  as host:port , the port will not be included
2003      */
2004     virtual String getHost() const;
2006     /**
2007      *
2008      */
2009     virtual int getPort() const;
2011     /**
2012      *
2013      */
2014     virtual String getPath() const;
2016     /**
2017      *
2018      */
2019     virtual String getNativePath() const;
2021     /**
2022      *
2023      */
2024     virtual bool isAbsolute() const;
2026     /**
2027      *
2028      */
2029     virtual bool isOpaque() const;
2031     /**
2032      *
2033      */
2034     virtual String getQuery() const;
2036     /**
2037      *
2038      */
2039     virtual String getFragment() const;
2041     /**
2042      *
2043      */
2044     virtual URI resolve(const URI &other) const;
2046     /**
2047      *
2048      */
2049     virtual void normalize();
2051 private:
2053     /**
2054      *
2055      */
2056     void init()
2057         {
2058         parsebuf  = NULL;
2059         parselen  = 0;
2060         scheme    = SCHEME_NONE;
2061         schemeStr = "";
2062         port      = 0;
2063         authority = "";
2064         path      = "";
2065         absolute  = false;
2066         opaque    = false;
2067         query     = "";
2068         fragment  = "";
2069         }
2072     /**
2073      *
2074      */
2075     void assign(const URI &other)
2076         {
2077         scheme    = other.scheme;
2078         schemeStr = other.schemeStr;
2079         authority = other.authority;
2080         port      = other.port;
2081         path      = other.path;
2082         absolute  = other.absolute;
2083         opaque    = other.opaque;
2084         query     = other.query;
2085         fragment  = other.fragment;
2086         }
2088     int scheme;
2090     String schemeStr;
2092     String authority;
2094     bool portSpecified;
2096     int port;
2098     String path;
2100     bool absolute;
2102     bool opaque;
2104     String query;
2106     String fragment;
2108     void error(const char *fmt, ...);
2110     void trace(const char *fmt, ...);
2113     int peek(int p);
2115     int match(int p, char *key);
2117     int parseScheme(int p);
2119     int parseHierarchicalPart(int p0);
2121     int parseQuery(int p0);
2123     int parseFragment(int p0);
2125     int parse(int p);
2127     char *parsebuf;
2129     int parselen;
2131 };
2135 typedef struct
2137     int  ival;
2138     char *sval;
2139     int  port;
2140 } LookupEntry;
2142 LookupEntry schemes[] =
2144     { URI::SCHEME_DATA,   "data:",    0 },
2145     { URI::SCHEME_HTTP,   "http:",   80 },
2146     { URI::SCHEME_HTTPS,  "https:", 443 },
2147     { URI::SCHEME_FTP,    "ftp",     12 },
2148     { URI::SCHEME_FILE,   "file:",    0 },
2149     { URI::SCHEME_LDAP,   "ldap:",  123 },
2150     { URI::SCHEME_MAILTO, "mailto:", 25 },
2151     { URI::SCHEME_NEWS,   "news:",  117 },
2152     { URI::SCHEME_TELNET, "telnet:", 23 },
2153     { 0,                  NULL,       0 }
2154 };
2157 String URI::toString() const
2159     String str = schemeStr;
2160     if (authority.size() > 0)
2161         {
2162         str.append("//");
2163         str.append(authority);
2164         }
2165     str.append(path);
2166     if (query.size() > 0)
2167         {
2168         str.append("?");
2169         str.append(query);
2170         }
2171     if (fragment.size() > 0)
2172         {
2173         str.append("#");
2174         str.append(fragment);
2175         }
2176     return str;
2180 int URI::getScheme() const
2182     return scheme;
2185 String URI::getSchemeStr() const
2187     return schemeStr;
2191 String URI::getAuthority() const
2193     String ret = authority;
2194     if (portSpecified && port>=0)
2195         {
2196         char buf[7];
2197         snprintf(buf, 6, ":%6d", port);
2198         ret.append(buf);
2199         }
2200     return ret;
2203 String URI::getHost() const
2205     return authority;
2208 int URI::getPort() const
2210     return port;
2214 String URI::getPath() const
2216     return path;
2219 String URI::getNativePath() const
2221     String npath;
2222 #ifdef __WIN32__
2223     unsigned int firstChar = 0;
2224     if (path.size() >= 3)
2225         {
2226         if (path[0] == '/' &&
2227             isLetter(path[1]) &&
2228             path[2] == ':')
2229             firstChar++;
2230          }
2231     for (unsigned int i=firstChar ; i<path.size() ; i++)
2232         {
2233         XMLCh ch = (XMLCh) path[i];
2234         if (ch == '/')
2235             npath.push_back((XMLCh)'\\');
2236         else
2237             npath.push_back(ch);
2238         }
2239 #else
2240     npath = path;
2241 #endif
2242     return npath;
2246 bool URI::isAbsolute() const
2248     return absolute;
2251 bool URI::isOpaque() const
2253     return opaque;
2257 String URI::getQuery() const
2259     return query;
2263 String URI::getFragment() const
2265     return fragment;
2269 URI URI::resolve(const URI &other) const
2271     //### According to w3c, this is handled in 3 cases
2273     //## 1
2274     if (opaque || other.isAbsolute())
2275         return other;
2277     //## 2
2278     if (other.fragment.size()  >  0 &&
2279         other.path.size()      == 0 &&
2280         other.scheme           == SCHEME_NONE &&
2281         other.authority.size() == 0 &&
2282         other.query.size()     == 0 )
2283         {
2284         URI fragUri = *this;
2285         fragUri.fragment = other.fragment;
2286         return fragUri;
2287         }
2289     //## 3 http://www.ietf.org/rfc/rfc2396.txt, section 5.2
2290     URI newUri;
2291     //# 3.1
2292     newUri.scheme    = scheme;
2293     newUri.schemeStr = schemeStr;
2294     newUri.query     = other.query;
2295     newUri.fragment  = other.fragment;
2296     if (other.authority.size() > 0)
2297         {
2298         //# 3.2
2299         if (absolute || other.absolute)
2300             newUri.absolute = true;
2301         newUri.authority = other.authority;
2302         newUri.port      = other.port;//part of authority
2303         newUri.path      = other.path;
2304         }
2305     else
2306         {
2307         //# 3.3
2308         if (other.absolute)
2309             {
2310             newUri.absolute = true;
2311             newUri.path     = other.path;
2312             }
2313         else
2314             {
2315             unsigned int pos = path.find_last_of('/');
2316             if (pos != path.npos)
2317                 {
2318                 String tpath = path.substr(0, pos+1);
2319                 tpath.append(other.path);
2320                 newUri.path = tpath;
2321                 }
2322             else
2323                 newUri.path = other.path;
2324             }
2325         }
2327     newUri.normalize();
2328     return newUri;
2332 /**
2333  *  This follows the Java URI algorithm:
2334  *   1. All "." segments are removed.
2335  *   2. If a ".." segment is preceded by a non-".." segment
2336  *          then both of these segments are removed. This step
2337  *          is repeated until it is no longer applicable.
2338  *   3. If the path is relative, and if its first segment
2339  *          contains a colon character (':'), then a "." segment
2340  *          is prepended. This prevents a relative URI with a path
2341  *          such as "a:b/c/d" from later being re-parsed as an
2342  *          opaque URI with a scheme of "a" and a scheme-specific
2343  *          part of "b/c/d". (Deviation from RFC 2396)
2344  */
2345 void URI::normalize()
2347     std::vector<String> segments;
2349     //## Collect segments
2350     if (path.size()<2)
2351         return;
2352     bool abs = false;
2353     unsigned int pos=0;
2354     if (path[0]=='/')
2355         {
2356         abs = true;
2357         pos++;
2358         }
2359     while (pos < path.size())
2360         {
2361         unsigned int pos2 = path.find('/', pos);
2362         if (pos2==path.npos)
2363             {
2364             String seg = path.substr(pos);
2365             //printf("last segment:%s\n", seg.c_str());
2366             segments.push_back(seg);
2367             break;
2368             }
2369         if (pos2>pos)
2370             {
2371             String seg = path.substr(pos, pos2-pos);
2372             //printf("segment:%s\n", seg.c_str());
2373             segments.push_back(seg);
2374             }
2375         pos = pos2;
2376         pos++;
2377         }
2379     //## Clean up (normalize) segments
2380     bool edited = false;
2381     std::vector<String>::iterator iter;
2382     for (iter=segments.begin() ; iter!=segments.end() ; )
2383         {
2384         String s = *iter;
2385         if (s == ".")
2386             {
2387             iter = segments.erase(iter);
2388             edited = true;
2389             }
2390         else if (s == ".." &&
2391                  iter != segments.begin() &&
2392                  *(iter-1) != "..")
2393             {
2394             iter--; //back up, then erase two entries
2395             iter = segments.erase(iter);
2396             iter = segments.erase(iter);
2397             edited = true;
2398             }
2399         else
2400             iter++;
2401         }
2403     //## Rebuild path, if necessary
2404     if (edited)
2405         {
2406         path.clear();
2407         if (abs)
2408             {
2409             path.append("/");
2410             }
2411         std::vector<String>::iterator iter;
2412         for (iter=segments.begin() ; iter!=segments.end() ; iter++)
2413             {
2414             if (iter != segments.begin())
2415                 path.append("/");
2416             path.append(*iter);
2417             }
2418         }
2424 //#########################################################################
2425 //# M E S S A G E S
2426 //#########################################################################
2428 void URI::error(const char *fmt, ...)
2430     va_list args;
2431     fprintf(stderr, "URI error: ");
2432     va_start(args, fmt);
2433     vfprintf(stderr, fmt, args);
2434     va_end(args);
2435     fprintf(stderr, "\n");
2438 void URI::trace(const char *fmt, ...)
2440     va_list args;
2441     fprintf(stdout, "URI: ");
2442     va_start(args, fmt);
2443     vfprintf(stdout, fmt, args);
2444     va_end(args);
2445     fprintf(stdout, "\n");
2450 //#########################################################################
2451 //# P A R S I N G
2452 //#########################################################################
2456 int URI::peek(int p)
2458     if (p<0 || p>=parselen)
2459         return -1;
2460     return parsebuf[p];
2465 int URI::match(int p0, char *key)
2467     int p = p0;
2468     while (p < parselen)
2469         {
2470         if (*key == '\0')
2471             return p;
2472         else if (*key != parsebuf[p])
2473             break;
2474         p++; key++;
2475         }
2476     return p0;
2479 //#########################################################################
2480 //#  Parsing is performed according to:
2481 //#  http://www.gbiv.com/protocols/uri/rfc/rfc3986.html#components
2482 //#########################################################################
2484 int URI::parseScheme(int p0)
2486     int p = p0;
2487     for (LookupEntry *entry = schemes; entry->sval ; entry++)
2488         {
2489         int p2 = match(p, entry->sval);
2490         if (p2 > p)
2491             {
2492             schemeStr = entry->sval;
2493             scheme    = entry->ival;
2494             port      = entry->port;
2495             p = p2;
2496             return p;
2497             }
2498         }
2500     return p;
2504 int URI::parseHierarchicalPart(int p0)
2506     int p = p0;
2507     int ch;
2509     //# Authority field (host and port, for example)
2510     int p2 = match(p, "//");
2511     if (p2 > p)
2512         {
2513         p = p2;
2514         portSpecified = false;
2515         String portStr;
2516         while (p < parselen)
2517             {
2518             ch = peek(p);
2519             if (ch == '/')
2520                 break;
2521             else if (ch == ':')
2522                 portSpecified = true;
2523             else if (portSpecified)
2524                 portStr.push_back((XMLCh)ch);
2525             else
2526                 authority.push_back((XMLCh)ch);
2527             p++;
2528             }
2529         if (portStr.size() > 0)
2530             {
2531             char *pstr = (char *)portStr.c_str();
2532             char *endStr;
2533             long val = strtol(pstr, &endStr, 10);
2534             if (endStr > pstr) //successful parse?
2535                 port = val;
2536             }
2537         }
2539     //# Are we absolute?
2540     ch = peek(p);
2541     if (isLetter(ch) && peek(p+1)==':')
2542         {
2543         absolute = true;
2544         path.push_back((XMLCh)'/');
2545         }
2546     else if (ch == '/')
2547         {
2548         absolute = true;
2549         if (p>p0) //in other words, if '/' is not the first char
2550             opaque = true;
2551         path.push_back((XMLCh)ch);
2552         p++;
2553         }
2555     while (p < parselen)
2556         {
2557         ch = peek(p);
2558         if (ch == '?' || ch == '#')
2559             break;
2560         path.push_back((XMLCh)ch);
2561         p++;
2562         }
2564     return p;
2567 int URI::parseQuery(int p0)
2569     int p = p0;
2570     int ch = peek(p);
2571     if (ch != '?')
2572         return p0;
2574     p++;
2575     while (p < parselen)
2576         {
2577         ch = peek(p);
2578         if (ch == '#')
2579             break;
2580         query.push_back((XMLCh)ch);
2581         p++;
2582         }
2585     return p;
2588 int URI::parseFragment(int p0)
2591     int p = p0;
2592     int ch = peek(p);
2593     if (ch != '#')
2594         return p0;
2596     p++;
2597     while (p < parselen)
2598         {
2599         ch = peek(p);
2600         if (ch == '?')
2601             break;
2602         fragment.push_back((XMLCh)ch);
2603         p++;
2604         }
2607     return p;
2611 int URI::parse(int p0)
2614     int p = p0;
2616     int p2 = parseScheme(p);
2617     if (p2 < 0)
2618         {
2619         error("Scheme");
2620         return -1;
2621         }
2622     p = p2;
2625     p2 = parseHierarchicalPart(p);
2626     if (p2 < 0)
2627         {
2628         error("Hierarchical part");
2629         return -1;
2630         }
2631     p = p2;
2633     p2 = parseQuery(p);
2634     if (p2 < 0)
2635         {
2636         error("Query");
2637         return -1;
2638         }
2639     p = p2;
2642     p2 = parseFragment(p);
2643     if (p2 < 0)
2644         {
2645         error("Fragment");
2646         return -1;
2647         }
2648     p = p2;
2650     return p;
2656 bool URI::parse(const String &str)
2658     init();
2659     
2660     parselen = str.size();
2662     String tmp;
2663     for (unsigned int i=0 ; i<str.size() ; i++)
2664         {
2665         XMLCh ch = (XMLCh) str[i];
2666         if (ch == '\\')
2667             tmp.push_back((XMLCh)'/');
2668         else
2669             tmp.push_back(ch);
2670         }
2671     parsebuf = (char *) tmp.c_str();
2674     int p = parse(0);
2675     normalize();
2677     if (p < 0)
2678         {
2679         error("Syntax error");
2680         return false;
2681         }
2683     //printf("uri:%s\n", toString().c_str());
2684     //printf("path:%s\n", path.c_str());
2686     return true;
2697 //########################################################################
2698 //########################################################################
2699 //##  M A K E
2700 //########################################################################
2701 //########################################################################
2703 //########################################################################
2704 //# F I L E S E T
2705 //########################################################################
2706 /**
2707  * This is the descriptor for a <fileset> item
2708  */
2709 class FileSet
2711 public:
2713     /**
2714      *
2715      */
2716     FileSet()
2717         {}
2719     /**
2720      *
2721      */
2722     FileSet(const FileSet &other)
2723         { assign(other); }
2725     /**
2726      *
2727      */
2728     FileSet &operator=(const FileSet &other)
2729         { assign(other); return *this; }
2731     /**
2732      *
2733      */
2734     virtual ~FileSet()
2735         {}
2737     /**
2738      *
2739      */
2740     String getDirectory()
2741         { return directory; }
2742         
2743     /**
2744      *
2745      */
2746     void setDirectory(const String &val)
2747         { directory = val; }
2749     /**
2750      *
2751      */
2752     void setFiles(const std::vector<String> &val)
2753         { files = val; }
2755     /**
2756      *
2757      */
2758     std::vector<String> getFiles()
2759         { return files; }
2760         
2761     /**
2762      *
2763      */
2764     void setIncludes(const std::vector<String> &val)
2765         { includes = val; }
2767     /**
2768      *
2769      */
2770     std::vector<String> getIncludes()
2771         { return includes; }
2772         
2773     /**
2774      *
2775      */
2776     void setExcludes(const std::vector<String> &val)
2777         { excludes = val; }
2779     /**
2780      *
2781      */
2782     std::vector<String> getExcludes()
2783         { return excludes; }
2784         
2785     /**
2786      *
2787      */
2788     unsigned int size()
2789         { return files.size(); }
2790         
2791     /**
2792      *
2793      */
2794     String operator[](int index)
2795         { return files[index]; }
2796         
2797     /**
2798      *
2799      */
2800     void clear()
2801         {
2802         directory = "";
2803         files.clear();
2804         includes.clear();
2805         excludes.clear();
2806         }
2807         
2809 private:
2811     void assign(const FileSet &other)
2812         {
2813         directory = other.directory;
2814         files     = other.files;
2815         includes  = other.includes;
2816         excludes  = other.excludes;
2817         }
2819     String directory;
2820     std::vector<String> files;
2821     std::vector<String> includes;
2822     std::vector<String> excludes;
2823 };
2828 //########################################################################
2829 //# M A K E    B A S E
2830 //########################################################################
2831 /**
2832  * Base class for all classes in this file
2833  */
2834 class MakeBase
2836 public:
2837     MakeBase()
2838         {}
2839     virtual ~MakeBase()
2840         {}
2842     /**
2843      *     Return the URI of the file associated with this object 
2844      */     
2845     URI getURI()
2846         { return uri; }
2848     /**
2849      * Set the uri to the given string
2850      */
2851     void setURI(const String &uristr)
2852         { uri.parse(uristr); }
2854     /**
2855      *  Resolve another path relative to this one
2856      */
2857     String resolve(const String &otherPath);
2859     /**
2860      *  Get an element attribute, performing substitutions if necessary
2861      */
2862     bool getAttribute(Element *elem, const String &name, String &result);
2864     /**
2865      * Get an element value, performing substitutions if necessary
2866      */
2867     bool getValue(Element *elem, String &result);
2869 protected:
2871     /**
2872      *    The path to the file associated with this object
2873      */     
2874     URI uri;
2877     /**
2878      *  Print a printf()-like formatted error message
2879      */
2880     void error(char *fmt, ...);
2882     /**
2883      *  Print a printf()-like formatted trace message
2884      */
2885     void status(char *fmt, ...);
2887     /**
2888      *  Print a printf()-like formatted trace message
2889      */
2890     void trace(char *fmt, ...);
2892     /**
2893      *  Check if a given string matches a given regex pattern
2894      */
2895     bool regexMatch(const String &str, const String &pattern);
2897     /**
2898      *
2899      */
2900     String getSuffix(const String &fname);
2902     /**
2903      * Break up a string into substrings delimited the characters
2904      * in delimiters.  Null-length substrings are ignored
2905      */  
2906     std::vector<String> tokenize(const String &val,
2907                           const String &delimiters);
2909     /**
2910      *  replace runs of whitespace with a space
2911      */
2912     String strip(const String &s);
2914     /**
2915      *  remove leading whitespace from each line
2916      */
2917     String leftJustify(const String &s);
2919     /**
2920      *  remove leading and trailing whitespace from string
2921      */
2922     String trim(const String &s);
2924     /**
2925      * Return the native format of the canonical
2926      * path which we store
2927      */
2928     String getNativePath(const String &path);
2930     /**
2931      * Execute a shell command.  Outbuf is a ref to a string
2932      * to catch the result.     
2933      */         
2934     bool executeCommand(const String &call,
2935                         const String &inbuf,
2936                         String &outbuf,
2937                         String &errbuf);
2938     /**
2939      * List all directories in a given base and starting directory
2940      * It is usually called like:
2941      *        bool ret = listDirectories("src", "", result);    
2942      */         
2943     bool listDirectories(const String &baseName,
2944                          const String &dirname,
2945                          std::vector<String> &res);
2947     /**
2948      * Find all files in the named directory 
2949      */         
2950     bool listFiles(const String &baseName,
2951                    const String &dirname,
2952                    std::vector<String> &result);
2954     /**
2955      * Perform a listing for a fileset 
2956      */         
2957     bool listFiles(MakeBase &propRef, FileSet &fileSet);
2959     /**
2960      * Parse a <patternset>
2961      */  
2962     bool parsePatternSet(Element *elem,
2963                        MakeBase &propRef,
2964                        std::vector<String> &includes,
2965                        std::vector<String> &excludes);
2967     /**
2968      * Parse a <fileset> entry, and determine which files
2969      * should be included
2970      */  
2971     bool parseFileSet(Element *elem,
2972                     MakeBase &propRef,
2973                     FileSet &fileSet);
2975     /**
2976      * Return this object's property list
2977      */
2978     virtual std::map<String, String> &getProperties()
2979         { return properties; }
2981     /**
2982      * Return a named property if found, else a null string
2983      */
2984     virtual String getProperty(const String &name)
2985         {
2986         String val;
2987         std::map<String, String>::iterator iter;
2988         iter = properties.find(name);
2989         if (iter != properties.end())
2990             val = iter->second;
2991         return val;
2992         }
2995     std::map<String, String> properties;
2997     /**
2998      * Turn 'true' and 'false' into boolean values
2999      */             
3000     bool getBool(const String &str, bool &val);
3002     /**
3003      * Create a directory, making intermediate dirs
3004      * if necessary
3005      */                  
3006     bool createDirectory(const String &dirname);
3008     /**
3009      * Delete a directory and its children if desired
3010      */
3011     bool removeDirectory(const String &dirName);
3013     /**
3014      * Copy a file from one name to another. Perform only if needed
3015      */ 
3016     bool copyFile(const String &srcFile, const String &destFile);
3018     /**
3019      * Tests if the file exists and is a regular file
3020      */ 
3021     bool isRegularFile(const String &fileName);
3023     /**
3024      * Tests if the file exists and is a directory
3025      */ 
3026     bool isDirectory(const String &fileName);
3028     /**
3029      * Tests is the modification date of fileA is newer than fileB
3030      */ 
3031     bool isNewerThan(const String &fileA, const String &fileB);
3033 private:
3035     /**
3036      * replace variable refs like ${a} with their values
3037      */         
3038     bool getSubstitutions(const String &s, String &result);
3042 };
3047 /**
3048  *  Print a printf()-like formatted error message
3049  */
3050 void MakeBase::error(char *fmt, ...)
3052     va_list args;
3053     va_start(args,fmt);
3054     fprintf(stderr, "Make error: ");
3055     vfprintf(stderr, fmt, args);
3056     fprintf(stderr, "\n");
3057     va_end(args) ;
3062 /**
3063  *  Print a printf()-like formatted trace message
3064  */
3065 void MakeBase::status(char *fmt, ...)
3067     va_list args;
3068     va_start(args,fmt);
3069     //fprintf(stdout, " ");
3070     vfprintf(stdout, fmt, args);
3071     fprintf(stdout, "\n");
3072     va_end(args) ;
3077 /**
3078  *  Resolve another path relative to this one
3079  */
3080 String MakeBase::resolve(const String &otherPath)
3082     URI otherURI(otherPath);
3083     URI fullURI = uri.resolve(otherURI);
3084     String ret = fullURI.toString();
3085     return ret;
3089 /**
3090  *  Print a printf()-like formatted trace message
3091  */
3092 void MakeBase::trace(char *fmt, ...)
3094     va_list args;
3095     va_start(args,fmt);
3096     fprintf(stdout, "Make: ");
3097     vfprintf(stdout, fmt, args);
3098     fprintf(stdout, "\n");
3099     va_end(args) ;
3104 /**
3105  *  Check if a given string matches a given regex pattern
3106  */
3107 bool MakeBase::regexMatch(const String &str, const String &pattern)
3109     const TRexChar *terror = NULL;
3110     const TRexChar *cpat = pattern.c_str();
3111     TRex *expr = trex_compile(cpat, &terror);
3112     if (!expr)
3113         {
3114         if (!terror)
3115             terror = "undefined";
3116         error("compilation error [%s]!\n", terror);
3117         return false;
3118         } 
3120     bool ret = true;
3122     const TRexChar *cstr = str.c_str();
3123     if (trex_match(expr, cstr))
3124         {
3125         ret = true;
3126         }
3127     else
3128         {
3129         ret = false;
3130         }
3132     trex_free(expr);
3134     return ret;
3137 /**
3138  *  Return the suffix, if any, of a file name
3139  */
3140 String MakeBase::getSuffix(const String &fname)
3142     if (fname.size() < 2)
3143         return "";
3144     unsigned int pos = fname.find_last_of('.');
3145     if (pos == fname.npos)
3146         return "";
3147     pos++;
3148     String res = fname.substr(pos, fname.size()-pos);
3149     //trace("suffix:%s", res.c_str()); 
3150     return res;
3155 /**
3156  * Break up a string into substrings delimited the characters
3157  * in delimiters.  Null-length substrings are ignored
3158  */  
3159 std::vector<String> MakeBase::tokenize(const String &str,
3160                                 const String &delimiters)
3163     std::vector<String> res;
3164     char *del = (char *)delimiters.c_str();
3165     String dmp;
3166     for (unsigned int i=0 ; i<str.size() ; i++)
3167         {
3168         char ch = str[i];
3169         char *p = (char *)0;
3170         for (p=del ; *p ; p++)
3171             if (*p == ch)
3172                 break;
3173         if (*p)
3174             {
3175             if (dmp.size() > 0)
3176                 {
3177                 res.push_back(dmp);
3178                 dmp.clear();
3179                 }
3180             }
3181         else
3182             {
3183             dmp.push_back(ch);
3184             }
3185         }
3186     //Add tail
3187     if (dmp.size() > 0)
3188         {
3189         res.push_back(dmp);
3190         dmp.clear();
3191         }
3193     return res;
3198 /**
3199  *  replace runs of whitespace with a single space
3200  */
3201 String MakeBase::strip(const String &s)
3203     int len = s.size();
3204     String stripped;
3205     for (int i = 0 ; i<len ; i++)
3206         {
3207         char ch = s[i];
3208         if (isspace(ch))
3209             {
3210             stripped.push_back(' ');
3211             for ( ; i<len ; i++)
3212                 {
3213                 ch = s[i];
3214                 if (!isspace(ch))
3215                     {
3216                     stripped.push_back(ch);
3217                     break;
3218                     }
3219                 }
3220             }
3221         else
3222             {
3223             stripped.push_back(ch);
3224             }
3225         }
3226     return stripped;
3229 /**
3230  *  remove leading whitespace from each line
3231  */
3232 String MakeBase::leftJustify(const String &s)
3234     String out;
3235     int len = s.size();
3236     for (int i = 0 ; i<len ; )
3237         {
3238         char ch;
3239         //Skip to first visible character
3240         while (i<len)
3241             {
3242             ch = s[i];
3243             if (ch == '\n' || ch == '\r'
3244               || !isspace(ch))
3245                   break;
3246             i++;
3247             }
3248         //Copy the rest of the line
3249         while (i<len)
3250             {
3251             ch = s[i];
3252             if (ch == '\n' || ch == '\r')
3253                 {
3254                 if (ch != '\r')
3255                     out.push_back('\n');
3256                 i++;
3257                 break;
3258                 }
3259             else
3260                 {
3261                 out.push_back(ch);
3262                 }
3263             i++;
3264             }
3265         }
3266     return out;
3270 /**
3271  *  Removes whitespace from beginning and end of a string
3272  */
3273 String MakeBase::trim(const String &s)
3275     if (s.size() < 1)
3276         return s;
3277     
3278     //Find first non-ws char
3279     unsigned int begin = 0;
3280     for ( ; begin < s.size() ; begin++)
3281         {
3282         if (!isspace(s[begin]))
3283             break;
3284         }
3286     //Find first non-ws char, going in reverse
3287     unsigned int end = s.size() - 1;
3288     for ( ; end > begin ; end--)
3289         {
3290         if (!isspace(s[end]))
3291             break;
3292         }
3293     //trace("begin:%d  end:%d", begin, end);
3295     String res = s.substr(begin, end-begin+1);
3296     return res;
3299 /**
3300  * Return the native format of the canonical
3301  * path which we store
3302  */
3303 String MakeBase::getNativePath(const String &path)
3305 #ifdef __WIN32__
3306     String npath;
3307     unsigned int firstChar = 0;
3308     if (path.size() >= 3)
3309         {
3310         if (path[0] == '/' &&
3311             isalpha(path[1]) &&
3312             path[2] == ':')
3313             firstChar++;
3314         }
3315     for (unsigned int i=firstChar ; i<path.size() ; i++)
3316         {
3317         char ch = path[i];
3318         if (ch == '/')
3319             npath.push_back('\\');
3320         else
3321             npath.push_back(ch);
3322         }
3323     return npath;
3324 #else
3325     return path;
3326 #endif
3330 #ifdef __WIN32__
3331 #include <tchar.h>
3333 static String win32LastError()
3336     DWORD dw = GetLastError(); 
3338     LPVOID str;
3339     FormatMessage(
3340         FORMAT_MESSAGE_ALLOCATE_BUFFER | 
3341         FORMAT_MESSAGE_FROM_SYSTEM,
3342         NULL,
3343         dw,
3344         0,
3345         (LPTSTR) &str,
3346         0, NULL );
3347     LPTSTR p = _tcschr((const char *)str, _T('\r'));
3348     if(p != NULL)
3349         { // lose CRLF
3350         *p = _T('\0');
3351         }
3352     String ret = (char *)str;
3353     LocalFree(str);
3355     return ret;
3357 #endif
3361 /**
3362  * Execute a system call, using pipes to send data to the
3363  * program's stdin,  and reading stdout and stderr.
3364  */
3365 bool MakeBase::executeCommand(const String &command,
3366                               const String &inbuf,
3367                               String &outbuf,
3368                               String &errbuf)
3371     status("============ cmd ============\n%s\n=============================",
3372                 command.c_str());
3374     outbuf.clear();
3375     errbuf.clear();
3376     
3377 #ifdef __WIN32__
3379     /*
3380     I really hate having win32 code in this program, but the
3381     read buffer in command.com and cmd.exe are just too small
3382     for the large commands we need for compiling and linking.
3383     */
3385     bool ret = true;
3387     //# Allocate a separate buffer for safety
3388     char *paramBuf = new char[command.size() + 1];
3389     if (!paramBuf)
3390        {
3391        error("executeCommand cannot allocate command buffer");
3392        return false;
3393        }
3394     strcpy(paramBuf, (char *)command.c_str());
3396     //# Create pipes
3397     SECURITY_ATTRIBUTES saAttr; 
3398     saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); 
3399     saAttr.bInheritHandle = TRUE; 
3400     saAttr.lpSecurityDescriptor = NULL; 
3401     HANDLE stdinRead,  stdinWrite;
3402     HANDLE stdoutRead, stdoutWrite;
3403     HANDLE stderrRead, stderrWrite;
3404     if (!CreatePipe(&stdinRead, &stdinWrite, &saAttr, 0))
3405         {
3406         error("executeProgram: could not create pipe");
3407         delete[] paramBuf;
3408         return false;
3409         } 
3410     SetHandleInformation(stdinWrite, HANDLE_FLAG_INHERIT, 0);
3411     if (!CreatePipe(&stdoutRead, &stdoutWrite, &saAttr, 0))
3412         {
3413         error("executeProgram: could not create pipe");
3414         delete[] paramBuf;
3415         return false;
3416         } 
3417     SetHandleInformation(stdoutRead, HANDLE_FLAG_INHERIT, 0);
3418     if (!CreatePipe(&stderrRead, &stderrWrite, &saAttr, 0))
3419         {
3420         error("executeProgram: could not create pipe");
3421         delete[] paramBuf;
3422         return false;
3423         } 
3424     SetHandleInformation(stderrRead, HANDLE_FLAG_INHERIT, 0);
3426     // Create the process
3427     STARTUPINFO siStartupInfo;
3428     PROCESS_INFORMATION piProcessInfo;
3429     memset(&siStartupInfo, 0, sizeof(siStartupInfo));
3430     memset(&piProcessInfo, 0, sizeof(piProcessInfo));
3431     siStartupInfo.cb = sizeof(siStartupInfo);
3432     siStartupInfo.hStdError   =  stderrWrite;
3433     siStartupInfo.hStdOutput  =  stdoutWrite;
3434     siStartupInfo.hStdInput   =  stdinRead;
3435     siStartupInfo.dwFlags    |=  STARTF_USESTDHANDLES;
3436    
3437     if (!CreateProcess(NULL, paramBuf, NULL, NULL, true,
3438                 0, NULL, NULL, &siStartupInfo,
3439                 &piProcessInfo))
3440         {
3441         error("executeCommand : could not create process : %s",
3442                     win32LastError().c_str());
3443         ret = false;
3444         }
3446     DWORD bytesWritten;
3447     if (inbuf.size()>0 &&
3448         !WriteFile(stdinWrite, inbuf.c_str(), inbuf.size(), 
3449                &bytesWritten, NULL))
3450         {
3451         error("executeCommand: could not write to pipe");
3452         return false;
3453         }    
3454     if (!CloseHandle(stdinWrite))
3455         {          
3456         error("executeCommand: could not close write pipe");
3457         return false;
3458         }
3459     if (!CloseHandle(stdoutWrite))
3460         {
3461         error("executeCommand: could not close read pipe");
3462         return false;
3463         }
3464     if (!CloseHandle(stderrWrite))
3465         {
3466         error("executeCommand: could not close read pipe");
3467         return false;
3468         }
3469     while (true)
3470         {
3471         //trace("## stderr");
3472         DWORD avail;
3473         if (!PeekNamedPipe(stderrRead, NULL, 0, NULL, &avail, NULL))
3474             break;
3475         if (avail > 0)
3476             {
3477             DWORD bytesRead = 0;
3478             char readBuf[1025];
3479             if (avail>1024) avail = 1024;
3480             if (!ReadFile(stderrRead, readBuf, avail, &bytesRead, NULL)
3481                 || bytesRead == 0)
3482                 {
3483                 break;
3484                 }
3485             for (unsigned int i=0 ; i<bytesRead ; i++)
3486                 errbuf.push_back(readBuf[i]);
3487             }
3488         }
3489     while (true)
3490         {
3491         //trace("## stdout");
3492         DWORD avail;
3493         if (!PeekNamedPipe(stdoutRead, NULL, 0, NULL, &avail, NULL))
3494             break;
3495         if (avail > 0)
3496             {
3497             DWORD bytesRead = 0;
3498             char readBuf[1025];
3499             if (avail>1024) avail = 1024;
3500             if (!ReadFile(stdoutRead, readBuf, avail, &bytesRead, NULL)
3501                 || bytesRead==0)
3502                 {
3503                 break;
3504                 }
3505             for (unsigned int i=0 ; i<bytesRead ; i++)
3506                 outbuf.push_back(readBuf[i]);
3507             }
3508         DWORD exitCode;
3509         GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
3510         if (exitCode != STILL_ACTIVE)
3511             break;
3512         Sleep(100);
3513         }    
3514     //trace("outbuf:%s", outbuf.c_str());
3515     if (!CloseHandle(stdoutRead))
3516         {
3517         error("executeCommand: could not close read pipe");
3518         return false;
3519         }
3520     if (!CloseHandle(stderrRead))
3521         {
3522         error("executeCommand: could not close read pipe");
3523         return false;
3524         }
3526     DWORD exitCode;
3527     GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
3528     //trace("exit code:%d", exitCode);
3529     if (exitCode != 0)
3530         {
3531         ret = false;
3532         }
3533     
3534     // Clean up
3535     CloseHandle(piProcessInfo.hProcess);
3536     CloseHandle(piProcessInfo.hThread);
3539     return ret;
3541 #else //do it unix-style
3543     String s;
3544     FILE *f = popen(command.c_str(), "r");
3545     int errnum = 0;
3546     if (f)
3547         {
3548         while (true)
3549             {
3550             int ch = fgetc(f);
3551             if (ch < 0)
3552                 break;
3553             s.push_back((char)ch);
3554             }
3555         errnum = pclose(f);
3556         }
3557     outbuf = s;
3558     if (errnum != 0)
3559         {
3560         error("exec of command '%s' failed : %s",
3561              command.c_str(), strerror(errno));
3562         return false;
3563         }
3564     else
3565         return true;
3567 #endif
3568
3573 bool MakeBase::listDirectories(const String &baseName,
3574                               const String &dirName,
3575                               std::vector<String> &res)
3577     res.push_back(dirName);
3578     String fullPath = baseName;
3579     if (dirName.size()>0)
3580         {
3581         fullPath.append("/");
3582         fullPath.append(dirName);
3583         }
3584     DIR *dir = opendir(fullPath.c_str());
3585     while (true)
3586         {
3587         struct dirent *de = readdir(dir);
3588         if (!de)
3589             break;
3591         //Get the directory member name
3592         String s = de->d_name;
3593         if (s.size() == 0 || s[0] == '.')
3594             continue;
3595         String childName = dirName;
3596         childName.append("/");
3597         childName.append(s);
3599         String fullChildPath = baseName;
3600         fullChildPath.append("/");
3601         fullChildPath.append(childName);
3602         struct stat finfo;
3603         String childNative = getNativePath(fullChildPath);
3604         if (stat(childNative.c_str(), &finfo)<0)
3605             {
3606             error("cannot stat file:%s", childNative.c_str());
3607             }
3608         else if (S_ISDIR(finfo.st_mode))
3609             {
3610             //trace("directory: %s", childName.c_str());
3611             if (!listDirectories(baseName, childName, res))
3612                 return false;
3613             }
3614         }
3615     closedir(dir);
3617     return true;
3621 bool MakeBase::listFiles(const String &baseDir,
3622                          const String &dirName,
3623                          std::vector<String> &res)
3625     String fullDir = baseDir;
3626     if (dirName.size()>0)
3627         {
3628         fullDir.append("/");
3629         fullDir.append(dirName);
3630         }
3631     String dirNative = getNativePath(fullDir);
3633     std::vector<String> subdirs;
3634     DIR *dir = opendir(dirNative.c_str());
3635     if (!dir)
3636         {
3637         error("Could not open directory %s : %s",
3638               dirNative.c_str(), strerror(errno));
3639         return false;
3640         }
3641     while (true)
3642         {
3643         struct dirent *de = readdir(dir);
3644         if (!de)
3645             break;
3647         //Get the directory member name
3648         String s = de->d_name;
3649         if (s.size() == 0 || s[0] == '.')
3650             continue;
3651         String childName;
3652         if (dirName.size()>0)
3653             {
3654             childName.append(dirName);
3655             childName.append("/");
3656             }
3657         childName.append(s);
3658         String fullChild = baseDir;
3659         fullChild.append("/");
3660         fullChild.append(childName);
3661         
3662         if (isDirectory(fullChild))
3663             {
3664             //trace("directory: %s", childName.c_str());
3665             if (!listFiles(baseDir, childName, res))
3666                 return false;
3667             continue;
3668             }
3669         else if (!isRegularFile(fullChild))
3670             {
3671             error("unknown file:%s", childName.c_str());
3672             return false;
3673             }
3675        //all done!
3676         res.push_back(childName);
3678         }
3679     closedir(dir);
3681     return true;
3685 bool MakeBase::listFiles(MakeBase &propRef, FileSet &fileSet)
3687     String baseDir = propRef.resolve(fileSet.getDirectory());
3688     std::vector<String> fileList;
3689     if (!listFiles(baseDir, "", fileList))
3690         return false;
3692     std::vector<String> includes = fileSet.getIncludes();
3693     std::vector<String> excludes = fileSet.getExcludes();
3695     std::vector<String> incs;
3696     std::vector<String>::iterator iter;
3698     std::sort(fileList.begin(), fileList.end());
3700     //If there are <includes>, then add files to the output
3701     //in the order of the include list
3702     if (includes.size()==0)
3703         incs = fileList;
3704     else
3705         {
3706         for (iter = includes.begin() ; iter != includes.end() ; iter++)
3707             {
3708             String pattern = *iter;
3709             std::vector<String>::iterator siter;
3710             for (siter = fileList.begin() ; siter != fileList.end() ; siter++)
3711                 {
3712                 String s = *siter;
3713                 if (regexMatch(s, pattern))
3714                     {
3715                     //trace("INCLUDED:%s", s.c_str());
3716                     incs.push_back(s);
3717                     }
3718                 }
3719             }
3720         }
3722     //Now trim off the <excludes>
3723     std::vector<String> res;
3724     for (iter = incs.begin() ; iter != incs.end() ; iter++)
3725         {
3726         String s = *iter;
3727         bool skipme = false;
3728         std::vector<String>::iterator siter;
3729         for (siter = excludes.begin() ; siter != excludes.end() ; siter++)
3730             {
3731             String pattern = *siter;
3732             if (regexMatch(s, pattern))
3733                 {
3734                 //trace("EXCLUDED:%s", s.c_str());
3735                 skipme = true;
3736                 break;
3737                 }
3738             }
3739         if (!skipme)
3740             res.push_back(s);
3741         }
3742         
3743     fileSet.setFiles(res);
3745     return true;
3752 bool MakeBase::getSubstitutions(const String &str, String &result)
3754     String s = trim(str);
3755     int len = (int)s.size();
3756     String val;
3757     for (int i=0 ; i<len ; i++)
3758         {
3759         char ch = s[i];
3760         if (ch == '$' && s[i+1] == '{')
3761             {
3762             String varname;
3763             int j = i+2;
3764             for ( ; j<len ; j++)
3765                 {
3766                 ch = s[j];
3767                 if (ch == '$' && s[j+1] == '{')
3768                     {
3769                     error("attribute %s cannot have nested variable references",
3770                            s.c_str());
3771                     return false;
3772                     }
3773                 else if (ch == '}')
3774                     {
3775                     std::map<String, String>::iterator iter;
3776                     iter = properties.find(trim(varname));
3777                     if (iter != properties.end())
3778                         {
3779                         val.append(iter->second);
3780                         }
3781                     else
3782                         {
3783                         error("property ${%s} not found", varname.c_str());
3784                         return false;
3785                         }
3786                     break;
3787                     }
3788                 else
3789                     {
3790                     varname.push_back(ch);
3791                     }
3792                 }
3793             i = j;
3794             }
3795         else
3796             {
3797             val.push_back(ch);
3798             }
3799         }
3800     result = val;
3801     return true;
3805 bool MakeBase::getAttribute(Element *elem, const String &name,
3806                                     String &result)
3808     String s = elem->getAttribute(name);
3809     return getSubstitutions(s, result);
3813 bool MakeBase::getValue(Element *elem, String &result)
3815     String s = elem->getValue();
3816     //Replace all runs of whitespace with a single space
3817     return getSubstitutions(s, result);
3821 /**
3822  * Turn 'true' and 'false' into boolean values
3823  */             
3824 bool MakeBase::getBool(const String &str, bool &val)
3826     if (str == "true")
3827         val = true;
3828     else if (str == "false")
3829         val = false;
3830     else
3831         {
3832         error("expected 'true' or 'false'.  found '%s'", str.c_str());
3833         return false;
3834         }
3835     return true;
3841 /**
3842  * Parse a <patternset> entry
3843  */  
3844 bool MakeBase::parsePatternSet(Element *elem,
3845                           MakeBase &propRef,
3846                           std::vector<String> &includes,
3847                           std::vector<String> &excludes
3848                           )
3850     std::vector<Element *> children  = elem->getChildren();
3851     for (unsigned int i=0 ; i<children.size() ; i++)
3852         {
3853         Element *child = children[i];
3854         String tagName = child->getName();
3855         if (tagName == "exclude")
3856             {
3857             String fname;
3858             if (!propRef.getAttribute(child, "name", fname))
3859                 return false;
3860             //trace("EXCLUDE: %s", fname.c_str());
3861             excludes.push_back(fname);
3862             }
3863         else if (tagName == "include")
3864             {
3865             String fname;
3866             if (!propRef.getAttribute(child, "name", fname))
3867                 return false;
3868             //trace("INCLUDE: %s", fname.c_str());
3869             includes.push_back(fname);
3870             }
3871         }
3873     return true;
3879 /**
3880  * Parse a <fileset> entry, and determine which files
3881  * should be included
3882  */  
3883 bool MakeBase::parseFileSet(Element *elem,
3884                           MakeBase &propRef,
3885                           FileSet &fileSet)
3887     String name = elem->getName();
3888     if (name != "fileset")
3889         {
3890         error("expected <fileset>");
3891         return false;
3892         }
3895     std::vector<String> includes;
3896     std::vector<String> excludes;
3898     //A fileset has one implied patternset
3899     if (!parsePatternSet(elem, propRef, includes, excludes))
3900         {
3901         return false;
3902         }
3903     //Look for child tags, including more patternsets
3904     std::vector<Element *> children  = elem->getChildren();
3905     for (unsigned int i=0 ; i<children.size() ; i++)
3906         {
3907         Element *child = children[i];
3908         String tagName = child->getName();
3909         if (tagName == "patternset")
3910             {
3911             if (!parsePatternSet(child, propRef, includes, excludes))
3912                 {
3913                 return false;
3914                 }
3915             }
3916         }
3918     String dir;
3919     //Now do the stuff
3920     //Get the base directory for reading file names
3921     if (!propRef.getAttribute(elem, "dir", dir))
3922         return false;
3924     fileSet.setDirectory(dir);
3925     fileSet.setIncludes(includes);
3926     fileSet.setExcludes(excludes);
3927     
3928     /*
3929     std::vector<String> fileList;
3930     if (dir.size() > 0)
3931         {
3932         String baseDir = propRef.resolve(dir);
3933         if (!listFiles(baseDir, "", includes, excludes, fileList))
3934             return false;
3935         }
3936     std::sort(fileList.begin(), fileList.end());
3937     result = fileList;
3938     */
3940     
3941     /*
3942     for (unsigned int i=0 ; i<result.size() ; i++)
3943         {
3944         trace("RES:%s", result[i].c_str());
3945         }
3946     */
3948     
3949     return true;
3954 /**
3955  * Create a directory, making intermediate dirs
3956  * if necessary
3957  */                  
3958 bool MakeBase::createDirectory(const String &dirname)
3960     //trace("## createDirectory: %s", dirname.c_str());
3961     //## first check if it exists
3962     struct stat finfo;
3963     String nativeDir = getNativePath(dirname);
3964     char *cnative = (char *) nativeDir.c_str();
3965 #ifdef __WIN32__
3966     if (strlen(cnative)==2 && cnative[1]==':')
3967         return true;
3968 #endif
3969     if (stat(cnative, &finfo)==0)
3970         {
3971         if (!S_ISDIR(finfo.st_mode))
3972             {
3973             error("mkdir: file %s exists but is not a directory",
3974                   cnative);
3975             return false;
3976             }
3977         else //exists
3978             {
3979             return true;
3980             }
3981         }
3983     //## 2: pull off the last path segment, if any,
3984     //## to make the dir 'above' this one, if necessary
3985     unsigned int pos = dirname.find_last_of('/');
3986     if (pos>0 && pos != dirname.npos)
3987         {
3988         String subpath = dirname.substr(0, pos);
3989         //A letter root (c:) ?
3990         if (!createDirectory(subpath))
3991             return false;
3992         }
3993         
3994     //## 3: now make
3995 #ifdef __WIN32__
3996     if (mkdir(cnative)<0)
3997 #else
3998     if (mkdir(cnative, S_IRWXU | S_IRWXG | S_IRWXO)<0)
3999 #endif
4000         {
4001         error("cannot make directory '%s' : %s",
4002                  cnative, strerror(errno));
4003         return false;
4004         }
4005         
4006     return true;
4010 /**
4011  * Remove a directory recursively
4012  */ 
4013 bool MakeBase::removeDirectory(const String &dirName)
4015     char *dname = (char *)dirName.c_str();
4017     DIR *dir = opendir(dname);
4018     if (!dir)
4019         {
4020         //# Let this fail nicely.
4021         return true;
4022         //error("error opening directory %s : %s", dname, strerror(errno));
4023         //return false;
4024         }
4025     
4026     while (true)
4027         {
4028         struct dirent *de = readdir(dir);
4029         if (!de)
4030             break;
4032         //Get the directory member name
4033         String s = de->d_name;
4034         if (s.size() == 0 || s[0] == '.')
4035             continue;
4036         String childName;
4037         if (dirName.size() > 0)
4038             {
4039             childName.append(dirName);
4040             childName.append("/");
4041             }
4042         childName.append(s);
4045         struct stat finfo;
4046         String childNative = getNativePath(childName);
4047         char *cnative = (char *)childNative.c_str();
4048         if (stat(cnative, &finfo)<0)
4049             {
4050             error("cannot stat file:%s", cnative);
4051             }
4052         else if (S_ISDIR(finfo.st_mode))
4053             {
4054             //trace("DEL dir: %s", childName.c_str());
4055             if (!removeDirectory(childName))
4056                 {
4057                 return false;
4058                 }
4059             }
4060         else if (!S_ISREG(finfo.st_mode))
4061             {
4062             //trace("not regular: %s", cnative);
4063             }
4064         else
4065             {
4066             //trace("DEL file: %s", childName.c_str());
4067             if (remove(cnative)<0)
4068                 {
4069                 error("error deleting %s : %s",
4070                      cnative, strerror(errno));
4071                 return false;
4072                 }
4073             }
4074         }
4075     closedir(dir);
4077     //Now delete the directory
4078     String native = getNativePath(dirName);
4079     if (rmdir(native.c_str())<0)
4080         {
4081         error("could not delete directory %s : %s",
4082             native.c_str() , strerror(errno));
4083         return false;
4084         }
4086     return true;
4087     
4091 /**
4092  * Copy a file from one name to another. Perform only if needed
4093  */ 
4094 bool MakeBase::copyFile(const String &srcFile, const String &destFile)
4096     //# 1 Check up-to-date times
4097     String srcNative = getNativePath(srcFile);
4098     struct stat srcinfo;
4099     if (stat(srcNative.c_str(), &srcinfo)<0)
4100         {
4101         error("source file %s for copy does not exist",
4102                  srcNative.c_str());
4103         return false;
4104         }
4106     String destNative = getNativePath(destFile);
4107     struct stat destinfo;
4108     if (stat(destNative.c_str(), &destinfo)==0)
4109         {
4110         if (destinfo.st_mtime >= srcinfo.st_mtime)
4111             return true;
4112         }
4113         
4114     //# 2 prepare a destination directory if necessary
4115     unsigned int pos = destFile.find_last_of('/');
4116     if (pos != destFile.npos)
4117         {
4118         String subpath = destFile.substr(0, pos);
4119         if (!createDirectory(subpath))
4120             return false;
4121         }
4123     //# 3 do the data copy
4124 #ifndef __WIN32__
4126     FILE *srcf = fopen(srcNative.c_str(), "rb");
4127     if (!srcf)
4128         {
4129         error("copyFile cannot open '%s' for reading", srcNative.c_str());
4130         return false;
4131         }
4132     FILE *destf = fopen(destNative.c_str(), "wb");
4133     if (!destf)
4134         {
4135         error("copyFile cannot open %s for writing", srcNative.c_str());
4136         return false;
4137         }
4139     while (!feof(srcf))
4140         {
4141         int ch = fgetc(srcf);
4142         if (ch<0)
4143             break;
4144         fputc(ch, destf);
4145         }
4147     fclose(destf);
4148     fclose(srcf);
4150 #else
4151     
4152     if (!CopyFile(srcNative.c_str(), destNative.c_str(), false))
4153         {
4154         error("copyFile from %s to %s failed",
4155              srcNative.c_str(), destNative.c_str());
4156         return false;
4157         }
4158         
4159 #endif /* __WIN32__ */
4162     return true;
4167 /**
4168  * Tests if the file exists and is a regular file
4169  */ 
4170 bool MakeBase::isRegularFile(const String &fileName)
4172     String native = getNativePath(fileName);
4173     struct stat finfo;
4174     
4175     //Exists?
4176     if (stat(native.c_str(), &finfo)<0)
4177         return false;
4180     //check the file mode
4181     if (!S_ISREG(finfo.st_mode))
4182         return false;
4184     return true;
4187 /**
4188  * Tests if the file exists and is a directory
4189  */ 
4190 bool MakeBase::isDirectory(const String &fileName)
4192     String native = getNativePath(fileName);
4193     struct stat finfo;
4194     
4195     //Exists?
4196     if (stat(native.c_str(), &finfo)<0)
4197         return false;
4200     //check the file mode
4201     if (!S_ISDIR(finfo.st_mode))
4202         return false;
4204     return true;
4209 /**
4210  * Tests is the modification of fileA is newer than fileB
4211  */ 
4212 bool MakeBase::isNewerThan(const String &fileA, const String &fileB)
4214     //trace("isNewerThan:'%s' , '%s'", fileA.c_str(), fileB.c_str());
4215     String nativeA = getNativePath(fileA);
4216     struct stat infoA;
4217     //IF source does not exist, NOT newer
4218     if (stat(nativeA.c_str(), &infoA)<0)
4219         {
4220         return false;
4221         }
4223     String nativeB = getNativePath(fileB);
4224     struct stat infoB;
4225     //IF dest does not exist, YES, newer
4226     if (stat(nativeB.c_str(), &infoB)<0)
4227         {
4228         return true;
4229         }
4231     //check the actual times
4232     if (infoA.st_mtime > infoB.st_mtime)
4233         {
4234         return true;
4235         }
4237     return false;
4241 //########################################################################
4242 //# P K G    C O N F I G
4243 //########################################################################
4245 /**
4246  *
4247  */
4248 class PkgConfig : public MakeBase
4251 public:
4253     /**
4254      *
4255      */
4256     PkgConfig()
4257         { init(); }
4259     /**
4260      *
4261      */
4262     PkgConfig(const String &namearg)
4263         { init(); name = namearg; }
4265     /**
4266      *
4267      */
4268     PkgConfig(const PkgConfig &other)
4269         { assign(other); }
4271     /**
4272      *
4273      */
4274     PkgConfig &operator=(const PkgConfig &other)
4275         { assign(other); return *this; }
4277     /**
4278      *
4279      */
4280     virtual ~PkgConfig()
4281         { }
4283     /**
4284      *
4285      */
4286     virtual String getName()
4287         { return name; }
4289     /**
4290      *
4291      */
4292     virtual String getDescription()
4293         { return description; }
4295     /**
4296      *
4297      */
4298     virtual String getCflags()
4299         { return cflags; }
4301     /**
4302      *
4303      */
4304     virtual String getLibs()
4305         { return libs; }
4307     /**
4308      *
4309      */
4310     virtual String getVersion()
4311         { return version; }
4313     /**
4314      *
4315      */
4316     virtual int getMajorVersion()
4317         { return majorVersion; }
4319     /**
4320      *
4321      */
4322     virtual int getMinorVersion()
4323         { return minorVersion; }
4325     /**
4326      *
4327      */
4328     virtual int getMicroVersion()
4329         { return microVersion; }
4331     /**
4332      *
4333      */
4334     virtual std::map<String, String> &getAttributes()
4335         { return attrs; }
4337     /**
4338      *
4339      */
4340     virtual std::vector<String> &getRequireList()
4341         { return requireList; }
4343     virtual bool readFile(const String &fileName);
4345 private:
4347     void init()
4348         {
4349         name         = "";
4350         description  = "";
4351         cflags       = "";
4352         libs         = "";
4353         requires     = "";
4354         version      = "";
4355         majorVersion = 0;
4356         minorVersion = 0;
4357         microVersion = 0;
4358         fileName     = "";
4359         attrs.clear();
4360         requireList.clear();
4361         }
4363     void assign(const PkgConfig &other)
4364         {
4365         name         = other.name;
4366         description  = other.description;
4367         cflags       = other.cflags;
4368         libs         = other.libs;
4369         requires     = other.requires;
4370         version      = other.version;
4371         majorVersion = other.majorVersion;
4372         minorVersion = other.minorVersion;
4373         microVersion = other.microVersion;
4374         fileName     = other.fileName;
4375         attrs        = other.attrs;
4376         requireList  = other.requireList;
4377         }
4381     int get(int pos);
4383     int skipwhite(int pos);
4385     int getword(int pos, String &ret);
4387     void parseRequires();
4389     void parseVersion();
4391     bool parse(const String &buf);
4393     void dumpAttrs();
4395     String name;
4397     String description;
4399     String cflags;
4401     String libs;
4403     String requires;
4405     String version;
4407     int majorVersion;
4409     int minorVersion;
4411     int microVersion;
4413     String fileName;
4415     std::map<String, String> attrs;
4417     std::vector<String> requireList;
4419     char *parsebuf;
4420     int parselen;
4421 };
4424 /**
4425  * Get a character from the buffer at pos.  If out of range,
4426  * return -1 for safety
4427  */
4428 int PkgConfig::get(int pos)
4430     if (pos>parselen)
4431         return -1;
4432     return parsebuf[pos];
4437 /**
4438  *  Skip over all whitespace characters beginning at pos.  Return
4439  *  the position of the first non-whitespace character.
4440  */
4441 int PkgConfig::skipwhite(int pos)
4443     while (pos < parselen)
4444         {
4445         int ch = get(pos);
4446         if (ch < 0)
4447             break;
4448         if (!isspace(ch))
4449             break;
4450         pos++;
4451         }
4452     return pos;
4456 /**
4457  *  Parse the buffer beginning at pos, for a word.  Fill
4458  *  'ret' with the result.  Return the position after the
4459  *  word.
4460  */
4461 int PkgConfig::getword(int pos, String &ret)
4463     while (pos < parselen)
4464         {
4465         int ch = get(pos);
4466         if (ch < 0)
4467             break;
4468         if (!isalnum(ch) && ch != '_' && ch != '-'&& ch != '.')
4469             break;
4470         ret.push_back((char)ch);
4471         pos++;
4472         }
4473     return pos;
4476 void PkgConfig::parseRequires()
4478     if (requires.size() == 0)
4479         return;
4480     parsebuf = (char *)requires.c_str();
4481     parselen = requires.size();
4482     int pos = 0;
4483     while (pos < parselen)
4484         {
4485         pos = skipwhite(pos);
4486         String val;
4487         int pos2 = getword(pos, val);
4488         if (pos2 == pos)
4489             break;
4490         pos = pos2;
4491         //trace("val %s", val.c_str());
4492         requireList.push_back(val);
4493         }
4496 static int getint(const String str)
4498     char *s = (char *)str.c_str();
4499     char *ends = NULL;
4500     long val = strtol(s, &ends, 10);
4501     if (ends == s)
4502         return 0L;
4503     else
4504         return val;
4507 void PkgConfig::parseVersion()
4509     if (version.size() == 0)
4510         return;
4511     String s1, s2, s3;
4512     unsigned int pos = 0;
4513     unsigned int pos2 = version.find('.', pos);
4514     if (pos2 == version.npos)
4515         {
4516         s1 = version;
4517         }
4518     else
4519         {
4520         s1 = version.substr(pos, pos2-pos);
4521         pos = pos2;
4522         pos++;
4523         if (pos < version.size())
4524             {
4525             pos2 = version.find('.', pos);
4526             if (pos2 == version.npos)
4527                 {
4528                 s2 = version.substr(pos, version.size()-pos);
4529                 }
4530             else
4531                 {
4532                 s2 = version.substr(pos, pos2-pos);
4533                 pos = pos2;
4534                 pos++;
4535                 if (pos < version.size())
4536                     s3 = version.substr(pos, pos2-pos);
4537                 }
4538             }
4539         }
4541     majorVersion = getint(s1);
4542     minorVersion = getint(s2);
4543     microVersion = getint(s3);
4544     //trace("version:%d.%d.%d", majorVersion,
4545     //          minorVersion, microVersion );
4549 bool PkgConfig::parse(const String &buf)
4551     init();
4553     parsebuf = (char *)buf.c_str();
4554     parselen = buf.size();
4555     int pos = 0;
4558     while (pos < parselen)
4559         {
4560         String attrName;
4561         pos = skipwhite(pos);
4562         int ch = get(pos);
4563         if (ch == '#')
4564             {
4565             //comment.  eat the rest of the line
4566             while (pos < parselen)
4567                 {
4568                 ch = get(pos);
4569                 if (ch == '\n' || ch < 0)
4570                     break;
4571                 pos++;
4572                 }
4573             continue;
4574             }
4575         pos = getword(pos, attrName);
4576         if (attrName.size() == 0)
4577             continue;
4578         pos = skipwhite(pos);
4579         ch = get(pos);
4580         if (ch != ':' && ch != '=')
4581             {
4582             error("expected ':' or '='");
4583             return false;
4584             }
4585         pos++;
4586         pos = skipwhite(pos);
4587         String attrVal;
4588         while (pos < parselen)
4589             {
4590             ch = get(pos);
4591             if (ch == '\n' || ch < 0)
4592                 break;
4593             else if (ch == '$' && get(pos+1) == '{')
4594                 {
4595                 //#  this is a ${substitution}
4596                 pos += 2;
4597                 String subName;
4598                 while (pos < parselen)
4599                     {
4600                     ch = get(pos);
4601                     if (ch < 0)
4602                         {
4603                         error("unterminated substitution");
4604                         return false;
4605                         }
4606                     else if (ch == '}')
4607                         break;
4608                     else
4609                         subName.push_back((char)ch);
4610                     pos++;
4611                     }
4612                 //trace("subName:%s", subName.c_str());
4613                 String subVal = attrs[subName];
4614                 //trace("subVal:%s", subVal.c_str());
4615                 attrVal.append(subVal);
4616                 }
4617             else
4618                 attrVal.push_back((char)ch);
4619             pos++;
4620             }
4622         attrVal = trim(attrVal);
4623         attrs[attrName] = attrVal;
4625         if (attrName == "Name")
4626             name = attrVal;
4627         else if (attrName == "Description")
4628             description = attrVal;
4629         else if (attrName == "Cflags")
4630             cflags = attrVal;
4631         else if (attrName == "Libs")
4632             libs = attrVal;
4633         else if (attrName == "Requires")
4634             requires = attrVal;
4635         else if (attrName == "Version")
4636             version = attrVal;
4638         //trace("name:'%s'  value:'%s'",
4639         //      attrName.c_str(), attrVal.c_str());
4640         }
4643     parseRequires();
4644     parseVersion();
4646     return true;
4649 void PkgConfig::dumpAttrs()
4651     //trace("### PkgConfig attributes for %s", fileName.c_str());
4652     std::map<String, String>::iterator iter;
4653     for (iter=attrs.begin() ; iter!=attrs.end() ; iter++)
4654         {
4655         trace("   %s = %s", iter->first.c_str(), iter->second.c_str());
4656         }
4660 bool PkgConfig::readFile(const String &fileNameArg)
4662     fileName = fileNameArg;
4664     FILE *f = fopen(fileName.c_str(), "r");
4665     if (!f)
4666         {
4667         error("cannot open file '%s' for reading", fileName.c_str());
4668         return false;
4669         }
4670     String buf;
4671     while (true)
4672         {
4673         int ch = fgetc(f);
4674         if (ch < 0)
4675             break;
4676         buf.push_back((char)ch);
4677         }
4678     fclose(f);
4680     //trace("####### File:\n%s", buf.c_str());
4681     if (!parse(buf))
4682         {
4683         return false;
4684         }
4686     dumpAttrs();
4688     return true;
4695 //########################################################################
4696 //# D E P T O O L
4697 //########################################################################
4701 /**
4702  *  Class which holds information for each file.
4703  */
4704 class FileRec
4706 public:
4708     typedef enum
4709         {
4710         UNKNOWN,
4711         CFILE,
4712         HFILE,
4713         OFILE
4714         } FileType;
4716     /**
4717      *  Constructor
4718      */
4719     FileRec()
4720         {init(); type = UNKNOWN;}
4722     /**
4723      *  Copy constructor
4724      */
4725     FileRec(const FileRec &other)
4726         {init(); assign(other);}
4727     /**
4728      *  Constructor
4729      */
4730     FileRec(int typeVal)
4731         {init(); type = typeVal;}
4732     /**
4733      *  Assignment operator
4734      */
4735     FileRec &operator=(const FileRec &other)
4736         {init(); assign(other); return *this;}
4739     /**
4740      *  Destructor
4741      */
4742     ~FileRec()
4743         {}
4745     /**
4746      *  Directory part of the file name
4747      */
4748     String path;
4750     /**
4751      *  Base name, sans directory and suffix
4752      */
4753     String baseName;
4755     /**
4756      *  File extension, such as cpp or h
4757      */
4758     String suffix;
4760     /**
4761      *  Type of file: CFILE, HFILE, OFILE
4762      */
4763     int type;
4765     /**
4766      * Used to list files ref'd by this one
4767      */
4768     std::map<String, FileRec *> files;
4771 private:
4773     void init()
4774         {
4775         }
4777     void assign(const FileRec &other)
4778         {
4779         type     = other.type;
4780         baseName = other.baseName;
4781         suffix   = other.suffix;
4782         files    = other.files;
4783         }
4785 };
4789 /**
4790  *  Simpler dependency record
4791  */
4792 class DepRec
4794 public:
4796     /**
4797      *  Constructor
4798      */
4799     DepRec()
4800         {init();}
4802     /**
4803      *  Copy constructor
4804      */
4805     DepRec(const DepRec &other)
4806         {init(); assign(other);}
4807     /**
4808      *  Constructor
4809      */
4810     DepRec(const String &fname)
4811         {init(); name = fname; }
4812     /**
4813      *  Assignment operator
4814      */
4815     DepRec &operator=(const DepRec &other)
4816         {init(); assign(other); return *this;}
4819     /**
4820      *  Destructor
4821      */
4822     ~DepRec()
4823         {}
4825     /**
4826      *  Directory part of the file name
4827      */
4828     String path;
4830     /**
4831      *  Base name, without the path and suffix
4832      */
4833     String name;
4835     /**
4836      *  Suffix of the source
4837      */
4838     String suffix;
4841     /**
4842      * Used to list files ref'd by this one
4843      */
4844     std::vector<String> files;
4847 private:
4849     void init()
4850         {
4851         }
4853     void assign(const DepRec &other)
4854         {
4855         path     = other.path;
4856         name     = other.name;
4857         suffix   = other.suffix;
4858         files    = other.files;
4859         }
4861 };
4864 class DepTool : public MakeBase
4866 public:
4868     /**
4869      *  Constructor
4870      */
4871     DepTool()
4872         {init();}
4874     /**
4875      *  Copy constructor
4876      */
4877     DepTool(const DepTool &other)
4878         {init(); assign(other);}
4880     /**
4881      *  Assignment operator
4882      */
4883     DepTool &operator=(const DepTool &other)
4884         {init(); assign(other); return *this;}
4887     /**
4888      *  Destructor
4889      */
4890     ~DepTool()
4891         {}
4894     /**
4895      *  Reset this section of code
4896      */
4897     virtual void init();
4898     
4899     /**
4900      *  Reset this section of code
4901      */
4902     virtual void assign(const DepTool &other)
4903         {
4904         }
4905     
4906     /**
4907      *  Sets the source directory which will be scanned
4908      */
4909     virtual void setSourceDirectory(const String &val)
4910         { sourceDir = val; }
4912     /**
4913      *  Returns the source directory which will be scanned
4914      */
4915     virtual String getSourceDirectory()
4916         { return sourceDir; }
4918     /**
4919      *  Sets the list of files within the directory to analyze
4920      */
4921     virtual void setFileList(const std::vector<String> &list)
4922         { fileList = list; }
4924     /**
4925      * Creates the list of all file names which will be
4926      * candidates for further processing.  Reads make.exclude
4927      * to see which files for directories to leave out.
4928      */
4929     virtual bool createFileList();
4932     /**
4933      *  Generates the forward dependency list
4934      */
4935     virtual bool generateDependencies();
4938     /**
4939      *  Generates the forward dependency list, saving the file
4940      */
4941     virtual bool generateDependencies(const String &);
4944     /**
4945      *  Load a dependency file
4946      */
4947     std::vector<DepRec> loadDepFile(const String &fileName);
4949     /**
4950      *  Load a dependency file, generating one if necessary
4951      */
4952     std::vector<DepRec> getDepFile(const String &fileName,
4953               bool forceRefresh);
4955     /**
4956      *  Save a dependency file
4957      */
4958     bool saveDepFile(const String &fileName);
4961 private:
4964     /**
4965      *
4966      */
4967     void parseName(const String &fullname,
4968                    String &path,
4969                    String &basename,
4970                    String &suffix);
4972     /**
4973      *
4974      */
4975     int get(int pos);
4977     /**
4978      *
4979      */
4980     int skipwhite(int pos);
4982     /**
4983      *
4984      */
4985     int getword(int pos, String &ret);
4987     /**
4988      *
4989      */
4990     bool sequ(int pos, char *key);
4992     /**
4993      *
4994      */
4995     bool addIncludeFile(FileRec *frec, const String &fname);
4997     /**
4998      *
4999      */
5000     bool scanFile(const String &fname, FileRec *frec);
5002     /**
5003      *
5004      */
5005     bool processDependency(FileRec *ofile,
5006                            FileRec *include,
5007                            int depth);
5009     /**
5010      *
5011      */
5012     String sourceDir;
5014     /**
5015      *
5016      */
5017     std::vector<String> fileList;
5019     /**
5020      *
5021      */
5022     std::vector<String> directories;
5024     /**
5025      * A list of all files which will be processed for
5026      * dependencies.  This is the only list that has the actual
5027      * records.  All other lists have pointers to these records.     
5028      */
5029     std::map<String, FileRec *> allFiles;
5031     /**
5032      * The list of .o files, and the
5033      * dependencies upon them.
5034      */
5035     std::map<String, FileRec *> depFiles;
5037     int depFileSize;
5038     char *depFileBuf;
5040     static const int readBufSize = 8192;
5041     char readBuf[8193];//byte larger
5043 };
5049 /**
5050  *  Clean up after processing.  Called by the destructor, but should
5051  *  also be called before the object is reused.
5052  */
5053 void DepTool::init()
5055     sourceDir = ".";
5057     fileList.clear();
5058     directories.clear();
5059     
5060     //clear refs
5061     depFiles.clear();
5062     //clear records
5063     std::map<String, FileRec *>::iterator iter;
5064     for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5065          delete iter->second;
5067     allFiles.clear(); 
5074 /**
5075  *  Parse a full path name into path, base name, and suffix
5076  */
5077 void DepTool::parseName(const String &fullname,
5078                         String &path,
5079                         String &basename,
5080                         String &suffix)
5082     if (fullname.size() < 2)
5083         return;
5085     unsigned int pos = fullname.find_last_of('/');
5086     if (pos != fullname.npos && pos<fullname.size()-1)
5087         {
5088         path = fullname.substr(0, pos);
5089         pos++;
5090         basename = fullname.substr(pos, fullname.size()-pos);
5091         }
5092     else
5093         {
5094         path = "";
5095         basename = fullname;
5096         }
5098     pos = basename.find_last_of('.');
5099     if (pos != basename.npos && pos<basename.size()-1)
5100         {
5101         suffix   = basename.substr(pos+1, basename.size()-pos-1);
5102         basename = basename.substr(0, pos);
5103         }
5105     //trace("parsename:%s %s %s", path.c_str(),
5106     //        basename.c_str(), suffix.c_str()); 
5111 /**
5112  *  Generate our internal file list.
5113  */
5114 bool DepTool::createFileList()
5117     for (unsigned int i=0 ; i<fileList.size() ; i++)
5118         {
5119         String fileName = fileList[i];
5120         //trace("## FileName:%s", fileName.c_str());
5121         String path;
5122         String basename;
5123         String sfx;
5124         parseName(fileName, path, basename, sfx);
5125         if (sfx == "cpp" || sfx == "c" || sfx == "cxx"   ||
5126             sfx == "cc" || sfx == "CC")
5127             {
5128             FileRec *fe         = new FileRec(FileRec::CFILE);
5129             fe->path            = path;
5130             fe->baseName        = basename;
5131             fe->suffix          = sfx;
5132             allFiles[fileName]  = fe;
5133             }
5134         else if (sfx == "h"   ||  sfx == "hh"  ||
5135                  sfx == "hpp" ||  sfx == "hxx")
5136             {
5137             FileRec *fe         = new FileRec(FileRec::HFILE);
5138             fe->path            = path;
5139             fe->baseName        = basename;
5140             fe->suffix          = sfx;
5141             allFiles[fileName]  = fe;
5142             }
5143         }
5145     if (!listDirectories(sourceDir, "", directories))
5146         return false;
5147         
5148     return true;
5155 /**
5156  * Get a character from the buffer at pos.  If out of range,
5157  * return -1 for safety
5158  */
5159 int DepTool::get(int pos)
5161     if (pos>depFileSize)
5162         return -1;
5163     return depFileBuf[pos];
5168 /**
5169  *  Skip over all whitespace characters beginning at pos.  Return
5170  *  the position of the first non-whitespace character.
5171  */
5172 int DepTool::skipwhite(int pos)
5174     while (pos < depFileSize)
5175         {
5176         int ch = get(pos);
5177         if (ch < 0)
5178             break;
5179         if (!isspace(ch))
5180             break;
5181         pos++;
5182         }
5183     return pos;
5187 /**
5188  *  Parse the buffer beginning at pos, for a word.  Fill
5189  *  'ret' with the result.  Return the position after the
5190  *  word.
5191  */
5192 int DepTool::getword(int pos, String &ret)
5194     while (pos < depFileSize)
5195         {
5196         int ch = get(pos);
5197         if (ch < 0)
5198             break;
5199         if (isspace(ch))
5200             break;
5201         ret.push_back((char)ch);
5202         pos++;
5203         }
5204     return pos;
5207 /**
5208  * Return whether the sequence of characters in the buffer
5209  * beginning at pos match the key,  for the length of the key
5210  */
5211 bool DepTool::sequ(int pos, char *key)
5213     while (*key)
5214         {
5215         if (*key != get(pos))
5216             return false;
5217         key++; pos++;
5218         }
5219     return true;
5224 /**
5225  *  Add an include file name to a file record.  If the name
5226  *  is not found in allFiles explicitly, try prepending include
5227  *  directory names to it and try again.
5228  */
5229 bool DepTool::addIncludeFile(FileRec *frec, const String &iname)
5232     std::map<String, FileRec *>::iterator iter =
5233            allFiles.find(iname);
5234     if (iter != allFiles.end()) //already exists
5235         {
5236          //h file in same dir
5237         FileRec *other = iter->second;
5238         //trace("local: '%s'", iname.c_str());
5239         frec->files[iname] = other;
5240         return true;
5241         }
5242     else 
5243         {
5244         //look in other dirs
5245         std::vector<String>::iterator diter;
5246         for (diter=directories.begin() ;
5247              diter!=directories.end() ; diter++)
5248             {
5249             String dfname = *diter;
5250             dfname.append("/");
5251             dfname.append(iname);
5252             iter = allFiles.find(dfname);
5253             if (iter != allFiles.end())
5254                 {
5255                 FileRec *other = iter->second;
5256                 //trace("other: '%s'", iname.c_str());
5257                 frec->files[dfname] = other;
5258                 return true;
5259                 }
5260             }
5261         }
5262     return true;
5267 /**
5268  *  Lightly parse a file to find the #include directives.  Do
5269  *  a bit of state machine stuff to make sure that the directive
5270  *  is valid.  (Like not in a comment).
5271  */
5272 bool DepTool::scanFile(const String &fname, FileRec *frec)
5274     String fileName;
5275     if (sourceDir.size() > 0)
5276         {
5277         fileName.append(sourceDir);
5278         fileName.append("/");
5279         }
5280     fileName.append(fname);
5281     String nativeName = getNativePath(fileName);
5282     FILE *f = fopen(nativeName.c_str(), "r");
5283     if (!f)
5284         {
5285         error("Could not open '%s' for reading", fname.c_str());
5286         return false;
5287         }
5288     String buf;
5289     while (!feof(f))
5290         {
5291         int len = fread(readBuf, 1, readBufSize, f);
5292         readBuf[len] = '\0';
5293         buf.append(readBuf);
5294         }
5295     fclose(f);
5297     depFileSize = buf.size();
5298     depFileBuf  = (char *)buf.c_str();
5299     int pos = 0;
5302     while (pos < depFileSize)
5303         {
5304         //trace("p:%c", get(pos));
5306         //# Block comment
5307         if (get(pos) == '/' && get(pos+1) == '*')
5308             {
5309             pos += 2;
5310             while (pos < depFileSize)
5311                 {
5312                 if (get(pos) == '*' && get(pos+1) == '/')
5313                     {
5314                     pos += 2;
5315                     break;
5316                     }
5317                 else
5318                     pos++;
5319                 }
5320             }
5321         //# Line comment
5322         else if (get(pos) == '/' && get(pos+1) == '/')
5323             {
5324             pos += 2;
5325             while (pos < depFileSize)
5326                 {
5327                 if (get(pos) == '\n')
5328                     {
5329                     pos++;
5330                     break;
5331                     }
5332                 else
5333                     pos++;
5334                 }
5335             }
5336         //# #include! yaay
5337         else if (sequ(pos, "#include"))
5338             {
5339             pos += 8;
5340             pos = skipwhite(pos);
5341             String iname;
5342             pos = getword(pos, iname);
5343             if (iname.size()>2)
5344                 {
5345                 iname = iname.substr(1, iname.size()-2);
5346                 addIncludeFile(frec, iname);
5347                 }
5348             }
5349         else
5350             {
5351             pos++;
5352             }
5353         }
5355     return true;
5360 /**
5361  *  Recursively check include lists to find all files in allFiles to which
5362  *  a given file is dependent.
5363  */
5364 bool DepTool::processDependency(FileRec *ofile,
5365                              FileRec *include,
5366                              int depth)
5368     std::map<String, FileRec *>::iterator iter;
5369     for (iter=include->files.begin() ; iter!=include->files.end() ; iter++)
5370         {
5371         String fname  = iter->first;
5372         if (ofile->files.find(fname) != ofile->files.end())
5373             {
5374             //trace("file '%s' already seen", fname.c_str());
5375             continue;
5376             }
5377         FileRec *child  = iter->second;
5378         ofile->files[fname] = child;
5379       
5380         processDependency(ofile, child, depth+1);
5381         }
5384     return true;
5391 /**
5392  *  Generate the file dependency list.
5393  */
5394 bool DepTool::generateDependencies()
5396     std::map<String, FileRec *>::iterator iter;
5397     //# First pass.  Scan for all includes
5398     for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5399         {
5400         FileRec *frec = iter->second;
5401         if (!scanFile(iter->first, frec))
5402             {
5403             //quit?
5404             }
5405         }
5407     //# Second pass.  Scan for all includes
5408     for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5409         {
5410         FileRec *include = iter->second;
5411         if (include->type == FileRec::CFILE)
5412             {
5413             String cFileName = iter->first;
5414             FileRec *ofile      = new FileRec(FileRec::OFILE);
5415             ofile->path         = include->path;
5416             ofile->baseName     = include->baseName;
5417             ofile->suffix       = include->suffix;
5418             String fname     = include->path;
5419             if (fname.size()>0)
5420                 fname.append("/");
5421             fname.append(include->baseName);
5422             fname.append(".o");
5423             depFiles[fname]    = ofile;
5424             //add the .c file first?   no, don't
5425             //ofile->files[cFileName] = include;
5426             
5427             //trace("ofile:%s", fname.c_str());
5429             processDependency(ofile, include, 0);
5430             }
5431         }
5433       
5434     return true;
5439 /**
5440  *  High-level call to generate deps and optionally save them
5441  */
5442 bool DepTool::generateDependencies(const String &fileName)
5444     if (!createFileList())
5445         return false;
5446     if (!generateDependencies())
5447         return false;
5448     if (!saveDepFile(fileName))
5449         return false;
5450     return true;
5454 /**
5455  *   This saves the dependency cache.
5456  */
5457 bool DepTool::saveDepFile(const String &fileName)
5459     time_t tim;
5460     time(&tim);
5462     FILE *f = fopen(fileName.c_str(), "w");
5463     if (!f)
5464         {
5465         trace("cannot open '%s' for writing", fileName.c_str());
5466         }
5467     fprintf(f, "<?xml version='1.0'?>\n");
5468     fprintf(f, "<!--\n");
5469     fprintf(f, "########################################################\n");
5470     fprintf(f, "## File: build.dep\n");
5471     fprintf(f, "## Generated by BuildTool at :%s", ctime(&tim));
5472     fprintf(f, "########################################################\n");
5473     fprintf(f, "-->\n");
5475     fprintf(f, "<dependencies source='%s'>\n\n", sourceDir.c_str());
5476     std::map<String, FileRec *>::iterator iter;
5477     for (iter=depFiles.begin() ; iter!=depFiles.end() ; iter++)
5478         {
5479         FileRec *frec = iter->second;
5480         if (frec->type == FileRec::OFILE)
5481             {
5482             fprintf(f, "<object path='%s' name='%s' suffix='%s'>\n",
5483                  frec->path.c_str(), frec->baseName.c_str(), frec->suffix.c_str());
5484             std::map<String, FileRec *>::iterator citer;
5485             for (citer=frec->files.begin() ; citer!=frec->files.end() ; citer++)
5486                 {
5487                 String cfname = citer->first;
5488                 fprintf(f, "    <dep name='%s'/>\n", cfname.c_str());
5489                 }
5490             fprintf(f, "</object>\n\n");
5491             }
5492         }
5494     fprintf(f, "</dependencies>\n");
5495     fprintf(f, "\n");
5496     fprintf(f, "<!--\n");
5497     fprintf(f, "########################################################\n");
5498     fprintf(f, "## E N D\n");
5499     fprintf(f, "########################################################\n");
5500     fprintf(f, "-->\n");
5502     fclose(f);
5504     return true;
5510 /**
5511  *   This loads the dependency cache.
5512  */
5513 std::vector<DepRec> DepTool::loadDepFile(const String &depFile)
5515     std::vector<DepRec> result;
5516     
5517     Parser parser;
5518     Element *root = parser.parseFile(depFile.c_str());
5519     if (!root)
5520         {
5521         //error("Could not open %s for reading", depFile.c_str());
5522         return result;
5523         }
5525     if (root->getChildren().size()==0 ||
5526         root->getChildren()[0]->getName()!="dependencies")
5527         {
5528         error("Main xml element should be <dependencies>");
5529         delete root;
5530         return result;
5531         }
5533     //########## Start parsing
5534     Element *depList = root->getChildren()[0];
5536     std::vector<Element *> objects = depList->getChildren();
5537     for (unsigned int i=0 ; i<objects.size() ; i++)
5538         {
5539         Element *objectElem = objects[i];
5540         String tagName = objectElem->getName();
5541         if (tagName == "object")
5542             {
5543             String objName   = objectElem->getAttribute("name");
5544              //trace("object:%s", objName.c_str());
5545             DepRec depObject(objName);
5546             depObject.path   = objectElem->getAttribute("path");
5547             depObject.suffix = objectElem->getAttribute("suffix");
5548             //########## DESCRIPTION
5549             std::vector<Element *> depElems = objectElem->getChildren();
5550             for (unsigned int i=0 ; i<depElems.size() ; i++)
5551                 {
5552                 Element *depElem = depElems[i];
5553                 tagName = depElem->getName();
5554                 if (tagName == "dep")
5555                     {
5556                     String depName = depElem->getAttribute("name");
5557                     //trace("    dep:%s", depName.c_str());
5558                     depObject.files.push_back(depName);
5559                     }
5560                 }
5561             //Insert into the result list, in a sorted manner
5562             bool inserted = false;
5563             std::vector<DepRec>::iterator iter;
5564             for (iter = result.begin() ; iter != result.end() ; iter++)
5565                 {
5566                 String vpath = iter->path;
5567                 vpath.append("/");
5568                 vpath.append(iter->name);
5569                 String opath = depObject.path;
5570                 opath.append("/");
5571                 opath.append(depObject.name);
5572                 if (vpath > opath)
5573                     {
5574                     inserted = true;
5575                     iter = result.insert(iter, depObject);
5576                     break;
5577                     }
5578                 }
5579             if (!inserted)
5580                 result.push_back(depObject);
5581             }
5582         }
5584     delete root;
5586     return result;
5590 /**
5591  *   This loads the dependency cache.
5592  */
5593 std::vector<DepRec> DepTool::getDepFile(const String &depFile,
5594                    bool forceRefresh)
5596     std::vector<DepRec> result;
5597     if (forceRefresh)
5598         {
5599         generateDependencies(depFile);
5600         result = loadDepFile(depFile);
5601         }
5602     else
5603         {
5604         //try once
5605         result = loadDepFile(depFile);
5606         if (result.size() == 0)
5607             {
5608             //fail? try again
5609             generateDependencies(depFile);
5610             result = loadDepFile(depFile);
5611             }
5612         }
5613     return result;
5619 //########################################################################
5620 //# T A S K
5621 //########################################################################
5622 //forward decl
5623 class Target;
5624 class Make;
5626 /**
5627  *
5628  */
5629 class Task : public MakeBase
5632 public:
5634     typedef enum
5635         {
5636         TASK_NONE,
5637         TASK_CC,
5638         TASK_COPY,
5639         TASK_DELETE,
5640         TASK_JAR,
5641         TASK_JAVAC,
5642         TASK_LINK,
5643         TASK_MAKEFILE,
5644         TASK_MKDIR,
5645         TASK_MSGFMT,
5646         TASK_RANLIB,
5647         TASK_RC,
5648         TASK_SHAREDLIB,
5649         TASK_STATICLIB,
5650         TASK_STRIP,
5651         TASK_TSTAMP
5652         } TaskType;
5653         
5655     /**
5656      *
5657      */
5658     Task(MakeBase &par) : parent(par)
5659         { init(); }
5661     /**
5662      *
5663      */
5664     Task(const Task &other) : parent(other.parent)
5665         { init(); assign(other); }
5667     /**
5668      *
5669      */
5670     Task &operator=(const Task &other)
5671         { assign(other); return *this; }
5673     /**
5674      *
5675      */
5676     virtual ~Task()
5677         { }
5680     /**
5681      *
5682      */
5683     virtual MakeBase &getParent()
5684         { return parent; }
5686      /**
5687      *
5688      */
5689     virtual int  getType()
5690         { return type; }
5692     /**
5693      *
5694      */
5695     virtual void setType(int val)
5696         { type = val; }
5698     /**
5699      *
5700      */
5701     virtual String getName()
5702         { return name; }
5704     /**
5705      *
5706      */
5707     virtual bool execute()
5708         { return true; }
5710     /**
5711      *
5712      */
5713     virtual bool parse(Element *elem)
5714         { return true; }
5716     /**
5717      *
5718      */
5719     Task *createTask(Element *elem);
5722 protected:
5724     void init()
5725         {
5726         type = TASK_NONE;
5727         name = "none";
5728         }
5730     void assign(const Task &other)
5731         {
5732         type = other.type;
5733         name = other.name;
5734         }
5735         
5736     String getAttribute(Element *elem, const String &attrName)
5737         {
5738         String str;
5739         return str;
5740         }
5742     MakeBase &parent;
5744     int type;
5746     String name;
5747 };
5751 /**
5752  * This task runs the C/C++ compiler.  The compiler is invoked
5753  * for all .c or .cpp files which are newer than their correcsponding
5754  * .o files.  
5755  */
5756 class TaskCC : public Task
5758 public:
5760     TaskCC(MakeBase &par) : Task(par)
5761         {
5762         type = TASK_CC; name = "cc";
5763         ccCommand   = "gcc";
5764         cxxCommand  = "g++";
5765         source      = ".";
5766         dest        = ".";
5767         flags       = "";
5768         defines     = "";
5769         includes    = "";
5770         fileSet.clear();
5771         }
5773     virtual ~TaskCC()
5774         {}
5776     virtual bool needsCompiling(const DepRec &depRec,
5777               const String &src, const String &dest)
5778         {
5779         return false;
5780         }
5782     virtual bool execute()
5783         {
5784         if (!listFiles(parent, fileSet))
5785             return false;
5787         bool refreshCache = false;
5788         String fullName = parent.resolve("build.dep");
5789         if (isNewerThan(parent.getURI().getPath(), fullName))
5790             {
5791             status("          : regenerating C/C++ dependency cache");
5792             refreshCache = true;
5793             }
5795         DepTool depTool;
5796         depTool.setSourceDirectory(source);
5797         depTool.setFileList(fileSet.getFiles());
5798         std::vector<DepRec> deps =
5799              depTool.getDepFile("build.dep", refreshCache);
5800         
5801         String incs;
5802         incs.append("-I");
5803         incs.append(parent.resolve("."));
5804         incs.append(" ");
5805         if (includes.size()>0)
5806             {
5807             incs.append(includes);
5808             incs.append(" ");
5809             }
5810         std::set<String> paths;
5811         std::vector<DepRec>::iterator viter;
5812         for (viter=deps.begin() ; viter!=deps.end() ; viter++)
5813             {
5814             DepRec dep = *viter;
5815             if (dep.path.size()>0)
5816                 paths.insert(dep.path);
5817             }
5818         if (source.size()>0)
5819             {
5820             incs.append(" -I");
5821             incs.append(parent.resolve(source));
5822             incs.append(" ");
5823             }
5824         std::set<String>::iterator setIter;
5825         for (setIter=paths.begin() ; setIter!=paths.end() ; setIter++)
5826             {
5827             incs.append(" -I");
5828             String dname;
5829             if (source.size()>0)
5830                 {
5831                 dname.append(source);
5832                 dname.append("/");
5833                 }
5834             dname.append(*setIter);
5835             incs.append(parent.resolve(dname));
5836             }
5837         std::vector<String> cfiles;
5838         for (viter=deps.begin() ; viter!=deps.end() ; viter++)
5839             {
5840             DepRec dep = *viter;
5842             //## Select command
5843             String sfx = dep.suffix;
5844             String command = ccCommand;
5845             if (sfx == "cpp" || sfx == "c++" || sfx == "cc"
5846                  || sfx == "CC")
5847                 command = cxxCommand;
5848  
5849             //## Make paths
5850             String destPath = dest;
5851             String srcPath  = source;
5852             if (dep.path.size()>0)
5853                 {
5854                 destPath.append("/");
5855                 destPath.append(dep.path);
5856                 srcPath.append("/");
5857                 srcPath.append(dep.path);
5858                 }
5859             //## Make sure destination directory exists
5860             if (!createDirectory(destPath))
5861                 return false;
5862                 
5863             //## Check whether it needs to be done
5864             String destName;
5865             if (destPath.size()>0)
5866                 {
5867                 destName.append(destPath);
5868                 destName.append("/");
5869                 }
5870             destName.append(dep.name);
5871             destName.append(".o");
5872             String destFullName = parent.resolve(destName);
5873             String srcName;
5874             if (srcPath.size()>0)
5875                 {
5876                 srcName.append(srcPath);
5877                 srcName.append("/");
5878                 }
5879             srcName.append(dep.name);
5880             srcName.append(".");
5881             srcName.append(dep.suffix);
5882             String srcFullName = parent.resolve(srcName);
5883             bool compileMe = false;
5884             if (isNewerThan(srcFullName, destFullName))
5885                 {
5886                 status("          : compile of %s required by %s",
5887                         destFullName.c_str(), srcFullName.c_str());
5888                 compileMe = true;
5889                 }
5890             else
5891                 {
5892                 for (unsigned int i=0 ; i<dep.files.size() ; i++)
5893                     {
5894                     String depName;
5895                     if (srcPath.size()>0)
5896                         {
5897                         depName.append(srcPath);
5898                         depName.append("/");
5899                         }
5900                     depName.append(dep.files[i]);
5901                     String depFullName = parent.resolve(depName);
5902                     if (isNewerThan(depFullName, destFullName))
5903                         {
5904                         status("          : compile of %s required by %s",
5905                                 destFullName.c_str(), depFullName.c_str());
5906                         compileMe = true;
5907                         break;
5908                         }
5909                     }
5910                 }
5911             if (!compileMe)
5912                 {
5913                 continue;
5914                 }
5916             //## Assemble the command
5917             String cmd = command;
5918             cmd.append(" -c ");
5919             cmd.append(flags);
5920             cmd.append(" ");
5921             cmd.append(defines);
5922             cmd.append(" ");
5923             cmd.append(incs);
5924             cmd.append(" ");
5925             cmd.append(srcFullName);
5926             cmd.append(" -o ");
5927             cmd.append(destFullName);
5929             //## Execute the command
5931             String outString, errString;
5932             if (!executeCommand(cmd.c_str(), "", outString, errString))
5933                 {
5934                 error("problem compiling: %s", errString.c_str());
5935                 return false;
5936                 }
5937             }
5938         
5939         return true;
5940         }
5942     virtual bool parse(Element *elem)
5943         {
5944         String s;
5945         if (!parent.getAttribute(elem, "command", s))
5946             return false;
5947         if (s.size()>0) { ccCommand = s; cxxCommand = s; }
5948         if (!parent.getAttribute(elem, "cc", s))
5949             return false;
5950         if (s.size()>0) ccCommand = s;
5951         if (!parent.getAttribute(elem, "cxx", s))
5952             return false;
5953         if (s.size()>0) cxxCommand = s;
5954         if (!parent.getAttribute(elem, "destdir", s))
5955             return false;
5956         if (s.size()>0) dest = s;
5958         std::vector<Element *> children = elem->getChildren();
5959         for (unsigned int i=0 ; i<children.size() ; i++)
5960             {
5961             Element *child = children[i];
5962             String tagName = child->getName();
5963             if (tagName == "flags")
5964                 {
5965                 if (!parent.getValue(child, flags))
5966                     return false;
5967                 flags = strip(flags);
5968                 }
5969             else if (tagName == "includes")
5970                 {
5971                 if (!parent.getValue(child, includes))
5972                     return false;
5973                 includes = strip(includes);
5974                 }
5975             else if (tagName == "defines")
5976                 {
5977                 if (!parent.getValue(child, defines))
5978                     return false;
5979                 defines = strip(defines);
5980                 }
5981             else if (tagName == "fileset")
5982                 {
5983                 if (!parseFileSet(child, parent, fileSet))
5984                     return false;
5985                 source = fileSet.getDirectory();
5986                 }
5987             }
5989         return true;
5990         }
5991         
5992 protected:
5994     String ccCommand;
5995     String cxxCommand;
5996     String source;
5997     String dest;
5998     String flags;
5999     String defines;
6000     String includes;
6001     FileSet fileSet;
6002     
6003 };
6007 /**
6008  *
6009  */
6010 class TaskCopy : public Task
6012 public:
6014     typedef enum
6015         {
6016         CP_NONE,
6017         CP_TOFILE,
6018         CP_TODIR
6019         } CopyType;
6021     TaskCopy(MakeBase &par) : Task(par)
6022         {
6023         type = TASK_COPY; name = "copy";
6024         cptype = CP_NONE;
6025         verbose = false;
6026         haveFileSet = false;
6027         }
6029     virtual ~TaskCopy()
6030         {}
6032     virtual bool execute()
6033         {
6034         switch (cptype)
6035            {
6036            case CP_TOFILE:
6037                {
6038                if (fileName.size()>0)
6039                    {
6040                    status("          : %s to %s",
6041                         fileName.c_str(), toFileName.c_str());
6042                    String fullSource = parent.resolve(fileName);
6043                    String fullDest = parent.resolve(toFileName);
6044                    //trace("copy %s to file %s", fullSource.c_str(),
6045                    //                       fullDest.c_str());
6046                    if (!isRegularFile(fullSource))
6047                        {
6048                        error("copy : file %s does not exist", fullSource.c_str());
6049                        return false;
6050                        }
6051                    if (!isNewerThan(fullSource, fullDest))
6052                        {
6053                        return true;
6054                        }
6055                    if (!copyFile(fullSource, fullDest))
6056                        return false;
6057                    status("          : 1 file copied");
6058                    }
6059                return true;
6060                }
6061            case CP_TODIR:
6062                {
6063                if (haveFileSet)
6064                    {
6065                    if (!listFiles(parent, fileSet))
6066                        return false;
6067                    String fileSetDir = fileSet.getDirectory();
6069                    status("          : %s to %s",
6070                        fileSetDir.c_str(), toDirName.c_str());
6072                    int nrFiles = 0;
6073                    for (unsigned int i=0 ; i<fileSet.size() ; i++)
6074                        {
6075                        String fileName = fileSet[i];
6077                        String sourcePath;
6078                        if (fileSetDir.size()>0)
6079                            {
6080                            sourcePath.append(fileSetDir);
6081                            sourcePath.append("/");
6082                            }
6083                        sourcePath.append(fileName);
6084                        String fullSource = parent.resolve(sourcePath);
6085                        
6086                        //Get the immediate parent directory's base name
6087                        String baseFileSetDir = fileSetDir;
6088                        unsigned int pos = baseFileSetDir.find_last_of('/');
6089                        if (pos!=baseFileSetDir.npos &&
6090                                   pos < baseFileSetDir.size()-1)
6091                            baseFileSetDir =
6092                               baseFileSetDir.substr(pos+1,
6093                                    baseFileSetDir.size());
6094                        //Now make the new path
6095                        String destPath;
6096                        if (toDirName.size()>0)
6097                            {
6098                            destPath.append(toDirName);
6099                            destPath.append("/");
6100                            }
6101                        if (baseFileSetDir.size()>0)
6102                            {
6103                            destPath.append(baseFileSetDir);
6104                            destPath.append("/");
6105                            }
6106                        destPath.append(fileName);
6107                        String fullDest = parent.resolve(destPath);
6108                        //trace("fileName:%s", fileName.c_str());
6109                        //trace("copy %s to new dir : %s", fullSource.c_str(),
6110                        //                   fullDest.c_str());
6111                        if (!isNewerThan(fullSource, fullDest))
6112                            {
6113                            //trace("copy skipping %s", fullSource.c_str());
6114                            continue;
6115                            }
6116                        if (!copyFile(fullSource, fullDest))
6117                            return false;
6118                        nrFiles++;
6119                        }
6120                    status("          : %d file(s) copied", nrFiles);
6121                    }
6122                else //file source
6123                    {
6124                    //For file->dir we want only the basename of
6125                    //the source appended to the dest dir
6126                    status("          : %s to %s", 
6127                        fileName.c_str(), toDirName.c_str());
6128                    String baseName = fileName;
6129                    unsigned int pos = baseName.find_last_of('/');
6130                    if (pos!=baseName.npos && pos<baseName.size()-1)
6131                        baseName = baseName.substr(pos+1, baseName.size());
6132                    String fullSource = parent.resolve(fileName);
6133                    String destPath;
6134                    if (toDirName.size()>0)
6135                        {
6136                        destPath.append(toDirName);
6137                        destPath.append("/");
6138                        }
6139                    destPath.append(baseName);
6140                    String fullDest = parent.resolve(destPath);
6141                    //trace("copy %s to new dir : %s", fullSource.c_str(),
6142                    //                       fullDest.c_str());
6143                    if (!isRegularFile(fullSource))
6144                        {
6145                        error("copy : file %s does not exist", fullSource.c_str());
6146                        return false;
6147                        }
6148                    if (!isNewerThan(fullSource, fullDest))
6149                        {
6150                        return true;
6151                        }
6152                    if (!copyFile(fullSource, fullDest))
6153                        return false;
6154                    status("          : 1 file copied");
6155                    }
6156                return true;
6157                }
6158            }
6159         return true;
6160         }
6163     virtual bool parse(Element *elem)
6164         {
6165         if (!parent.getAttribute(elem, "file", fileName))
6166             return false;
6167         if (!parent.getAttribute(elem, "tofile", toFileName))
6168             return false;
6169         if (toFileName.size() > 0)
6170             cptype = CP_TOFILE;
6171         if (!parent.getAttribute(elem, "todir", toDirName))
6172             return false;
6173         if (toDirName.size() > 0)
6174             cptype = CP_TODIR;
6175         String ret;
6176         if (!parent.getAttribute(elem, "verbose", ret))
6177             return false;
6178         if (ret.size()>0 && !getBool(ret, verbose))
6179             return false;
6180             
6181         haveFileSet = false;
6182         
6183         std::vector<Element *> children = elem->getChildren();
6184         for (unsigned int i=0 ; i<children.size() ; i++)
6185             {
6186             Element *child = children[i];
6187             String tagName = child->getName();
6188             if (tagName == "fileset")
6189                 {
6190                 if (!parseFileSet(child, parent, fileSet))
6191                     {
6192                     error("problem getting fileset");
6193                     return false;
6194                     }
6195                 haveFileSet = true;
6196                 }
6197             }
6199         //Perform validity checks
6200         if (fileName.size()>0 && fileSet.size()>0)
6201             {
6202             error("<copy> can only have one of : file= and <fileset>");
6203             return false;
6204             }
6205         if (toFileName.size()>0 && toDirName.size()>0)
6206             {
6207             error("<copy> can only have one of : tofile= or todir=");
6208             return false;
6209             }
6210         if (haveFileSet && toDirName.size()==0)
6211             {
6212             error("a <copy> task with a <fileset> must have : todir=");
6213             return false;
6214             }
6215         if (cptype == CP_TOFILE && fileName.size()==0)
6216             {
6217             error("<copy> tofile= must be associated with : file=");
6218             return false;
6219             }
6220         if (cptype == CP_TODIR && fileName.size()==0 && !haveFileSet)
6221             {
6222             error("<copy> todir= must be associated with : file= or <fileset>");
6223             return false;
6224             }
6226         return true;
6227         }
6228         
6229 private:
6231     int cptype;
6232     String fileName;
6233     FileSet fileSet;
6234     String toFileName;
6235     String toDirName;
6236     bool verbose;
6237     bool haveFileSet;
6238 };
6241 /**
6242  *
6243  */
6244 class TaskDelete : public Task
6246 public:
6248     typedef enum
6249         {
6250         DEL_FILE,
6251         DEL_DIR,
6252         DEL_FILESET
6253         } DeleteType;
6255     TaskDelete(MakeBase &par) : Task(par)
6256         { 
6257           type        = TASK_DELETE;
6258           name        = "delete";
6259           delType     = DEL_FILE;
6260           verbose     = false;
6261           quiet       = false;
6262           failOnError = true;
6263         }
6265     virtual ~TaskDelete()
6266         {}
6268     virtual bool execute()
6269         {
6270         struct stat finfo;
6271         switch (delType)
6272             {
6273             case DEL_FILE:
6274                 {
6275                 status("          : %s", fileName.c_str());
6276                 String fullName = parent.resolve(fileName);
6277                 char *fname = (char *)fullName.c_str();
6278                 //does not exist
6279                 if (stat(fname, &finfo)<0)
6280                     return true;
6281                 //exists but is not a regular file
6282                 if (!S_ISREG(finfo.st_mode))
6283                     {
6284                     error("<delete> failed. '%s' exists and is not a regular file",
6285                           fname);
6286                     return false;
6287                     }
6288                 if (remove(fname)<0)
6289                     {
6290                     error("<delete> failed: %s", strerror(errno));
6291                     return false;
6292                     }
6293                 return true;
6294                 }
6295             case DEL_DIR:
6296                 {
6297                 status("          : %s", dirName.c_str());
6298                 String fullDir = parent.resolve(dirName);
6299                 if (!removeDirectory(fullDir))
6300                     return false;
6301                 return true;
6302                 }
6303             }
6304         return true;
6305         }
6307     virtual bool parse(Element *elem)
6308         {
6309         if (!parent.getAttribute(elem, "file", fileName))
6310             return false;
6311         if (fileName.size() > 0)
6312             delType = DEL_FILE;
6313         if (!parent.getAttribute(elem, "dir", dirName))
6314             return false;
6315         if (dirName.size() > 0)
6316             delType = DEL_DIR;
6317         if (fileName.size()>0 && dirName.size()>0)
6318             {
6319             error("<delete> can only have one attribute of file= or dir=");
6320             return false;
6321             }
6322         String ret;
6323         if (!parent.getAttribute(elem, "verbose", ret))
6324             return false;
6325         if (ret.size()>0 && !getBool(ret, verbose))
6326             return false;
6327         if (!parent.getAttribute(elem, "quiet", ret))
6328             return false;
6329         if (ret.size()>0 && !getBool(ret, quiet))
6330             return false;
6331         if (!parent.getAttribute(elem, "failonerror", ret))
6332             return false;
6333         if (ret.size()>0 && !getBool(ret, failOnError))
6334             return false;
6335         return true;
6336         }
6338 private:
6340     int delType;
6341     String dirName;
6342     String fileName;
6343     bool verbose;
6344     bool quiet;
6345     bool failOnError;
6346 };
6349 /**
6350  *
6351  */
6352 class TaskJar : public Task
6354 public:
6356     TaskJar(MakeBase &par) : Task(par)
6357         { type = TASK_JAR; name = "jar"; }
6359     virtual ~TaskJar()
6360         {}
6362     virtual bool execute()
6363         {
6364         return true;
6365         }
6367     virtual bool parse(Element *elem)
6368         {
6369         return true;
6370         }
6371 };
6374 /**
6375  *
6376  */
6377 class TaskJavac : public Task
6379 public:
6381     TaskJavac(MakeBase &par) : Task(par)
6382         { type = TASK_JAVAC; name = "javac"; }
6384     virtual ~TaskJavac()
6385         {}
6387     virtual bool execute()
6388         {
6389         return true;
6390         }
6392     virtual bool parse(Element *elem)
6393         {
6394         return true;
6395         }
6396 };
6399 /**
6400  *
6401  */
6402 class TaskLink : public Task
6404 public:
6406     TaskLink(MakeBase &par) : Task(par)
6407         {
6408         type = TASK_LINK; name = "link";
6409         command = "g++";
6410         doStrip = false;
6411                 stripCommand = "strip";
6412                 objcopyCommand = "objcopy";
6413         }
6415     virtual ~TaskLink()
6416         {}
6418     virtual bool execute()
6419         {
6420         if (!listFiles(parent, fileSet))
6421             return false;
6422         String fileSetDir = fileSet.getDirectory();
6423         //trace("%d files in %s", fileSet.size(), fileSetDir.c_str());
6424         bool doit = false;
6425         String fullTarget = parent.resolve(fileName);
6426         String cmd = command;
6427         cmd.append(" -o ");
6428         cmd.append(fullTarget);
6429         cmd.append(" ");
6430         cmd.append(flags);
6431         for (unsigned int i=0 ; i<fileSet.size() ; i++)
6432             {
6433             cmd.append(" ");
6434             String obj;
6435             if (fileSetDir.size()>0)
6436                 {
6437                 obj.append(fileSetDir);
6438                 obj.append("/");
6439                 }
6440             obj.append(fileSet[i]);
6441             String fullObj = parent.resolve(obj);
6442             cmd.append(fullObj);
6443             //trace("link: tgt:%s obj:%s", fullTarget.c_str(),
6444             //          fullObj.c_str());
6445             if (isNewerThan(fullObj, fullTarget))
6446                 doit = true;
6447             }
6448         cmd.append(" ");
6449         cmd.append(libs);
6450         if (!doit)
6451             {
6452             //trace("link not needed");
6453             return true;
6454             }
6455         //trace("LINK cmd:%s", cmd.c_str());
6458         String outbuf, errbuf;
6459         if (!executeCommand(cmd.c_str(), "", outbuf, errbuf))
6460             {
6461             error("LINK problem: %s", errbuf.c_str());
6462             return false;
6463             }
6465         if (symFileName.size()>0)
6466             {
6467             String symFullName = parent.resolve(symFileName);
6468             cmd = objcopyCommand;
6469             cmd.append(" --only-keep-debug ");
6470             cmd.append(getNativePath(fullTarget));
6471             cmd.append(" ");
6472             cmd.append(getNativePath(symFullName));
6473             if (!executeCommand(cmd, "", outbuf, errbuf))
6474                 {
6475                 error("<strip> symbol file failed : %s", errbuf.c_str());
6476                 return false;
6477                 }
6478             }
6479             
6480         if (doStrip)
6481             {
6482             cmd = stripCommand;
6483             cmd.append(" ");
6484             cmd.append(getNativePath(fullTarget));
6485             if (!executeCommand(cmd, "", outbuf, errbuf))
6486                {
6487                error("<strip> failed : %s", errbuf.c_str());
6488                return false;
6489                }
6490             }
6492         return true;
6493         }
6495     virtual bool parse(Element *elem)
6496         {
6497         String s;
6498         if (!parent.getAttribute(elem, "command", s))
6499             return false;
6500         if (s.size()>0)
6501             command = s;
6502         if (!parent.getAttribute(elem, "objcopycommand", s))
6503             return false;
6504         if (s.size()>0)
6505             objcopyCommand = s;
6506         if (!parent.getAttribute(elem, "stripcommand", s))
6507             return false;
6508         if (s.size()>0)
6509             stripCommand = s;
6510         if (!parent.getAttribute(elem, "out", fileName))
6511             return false;
6512         if (!parent.getAttribute(elem, "strip", s))
6513             return false;
6514         if (s.size()>0 && !getBool(s, doStrip))
6515             return false;
6516         if (!parent.getAttribute(elem, "symfile", symFileName))
6517             return false;
6518             
6519         std::vector<Element *> children = elem->getChildren();
6520         for (unsigned int i=0 ; i<children.size() ; i++)
6521             {
6522             Element *child = children[i];
6523             String tagName = child->getName();
6524             if (tagName == "fileset")
6525                 {
6526                 if (!parseFileSet(child, parent, fileSet))
6527                     return false;
6528                 }
6529             else if (tagName == "flags")
6530                 {
6531                 if (!parent.getValue(child, flags))
6532                     return false;
6533                 flags = strip(flags);
6534                 }
6535             else if (tagName == "libs")
6536                 {
6537                 if (!parent.getValue(child, libs))
6538                     return false;
6539                 libs = strip(libs);
6540                 }
6541             }
6542         return true;
6543         }
6545 private:
6547     String  command;
6548     String  fileName;
6549     String  flags;
6550     String  libs;
6551     FileSet fileSet;
6552     bool    doStrip;
6553     String  symFileName;
6554     String  stripCommand;
6555     String  objcopyCommand;
6557 };
6561 /**
6562  * Create a named directory
6563  */
6564 class TaskMakeFile : public Task
6566 public:
6568     TaskMakeFile(MakeBase &par) : Task(par)
6569         { type = TASK_MAKEFILE; name = "makefile"; }
6571     virtual ~TaskMakeFile()
6572         {}
6574     virtual bool execute()
6575         {
6576         status("          : %s", fileName.c_str());
6577         String fullName = parent.resolve(fileName);
6578         if (!isNewerThan(parent.getURI().getPath(), fullName))
6579             {
6580             //trace("skipped <makefile>");
6581             return true;
6582             }
6583         //trace("fullName:%s", fullName.c_str());
6584         FILE *f = fopen(fullName.c_str(), "w");
6585         if (!f)
6586             {
6587             error("<makefile> could not open %s for writing : %s",
6588                 fullName.c_str(), strerror(errno));
6589             return false;
6590             }
6591         for (unsigned int i=0 ; i<text.size() ; i++)
6592             fputc(text[i], f);
6593         fputc('\n', f);
6594         fclose(f);
6595         return true;
6596         }
6598     virtual bool parse(Element *elem)
6599         {
6600         if (!parent.getAttribute(elem, "file", fileName))
6601             return false;
6602         if (fileName.size() == 0)
6603             {
6604             error("<makefile> requires 'file=\"filename\"' attribute");
6605             return false;
6606             }
6607         if (!parent.getValue(elem, text))
6608             return false;
6609         text = leftJustify(text);
6610         //trace("dirname:%s", dirName.c_str());
6611         return true;
6612         }
6614 private:
6616     String fileName;
6617     String text;
6618 };
6622 /**
6623  * Create a named directory
6624  */
6625 class TaskMkDir : public Task
6627 public:
6629     TaskMkDir(MakeBase &par) : Task(par)
6630         { type = TASK_MKDIR; name = "mkdir"; }
6632     virtual ~TaskMkDir()
6633         {}
6635     virtual bool execute()
6636         {
6637         status("          : %s", dirName.c_str());
6638         String fullDir = parent.resolve(dirName);
6639         //trace("fullDir:%s", fullDir.c_str());
6640         if (!createDirectory(fullDir))
6641             return false;
6642         return true;
6643         }
6645     virtual bool parse(Element *elem)
6646         {
6647         if (!parent.getAttribute(elem, "dir", dirName))
6648             return false;
6649         if (dirName.size() == 0)
6650             {
6651             error("<mkdir> requires 'dir=\"dirname\"' attribute");
6652             return false;
6653             }
6654         return true;
6655         }
6657 private:
6659     String dirName;
6660 };
6664 /**
6665  * Create a named directory
6666  */
6667 class TaskMsgFmt: public Task
6669 public:
6671     TaskMsgFmt(MakeBase &par) : Task(par)
6672          {
6673          type    = TASK_MSGFMT;
6674          name    = "msgfmt";
6675          command = "msgfmt";
6676          owndir  = false;
6677          outName = "";
6678          }
6680     virtual ~TaskMsgFmt()
6681         {}
6683     virtual bool execute()
6684         {
6685         if (!listFiles(parent, fileSet))
6686             return false;
6687         String fileSetDir = fileSet.getDirectory();
6689         //trace("msgfmt: %d", fileSet.size());
6690         for (unsigned int i=0 ; i<fileSet.size() ; i++)
6691             {
6692             String fileName = fileSet[i];
6693             if (getSuffix(fileName) != "po")
6694                 continue;
6695             String sourcePath;
6696             if (fileSetDir.size()>0)
6697                 {
6698                 sourcePath.append(fileSetDir);
6699                 sourcePath.append("/");
6700                 }
6701             sourcePath.append(fileName);
6702             String fullSource = parent.resolve(sourcePath);
6704             String destPath;
6705             if (toDirName.size()>0)
6706                 {
6707                 destPath.append(toDirName);
6708                 destPath.append("/");
6709                 }
6710             if (owndir)
6711                 {
6712                 String subdir = fileName;
6713                 unsigned int pos = subdir.find_last_of('.');
6714                 if (pos != subdir.npos)
6715                     subdir = subdir.substr(0, pos);
6716                 destPath.append(subdir);
6717                 destPath.append("/");
6718                 }
6719             //Pick the output file name
6720             if (outName.size() > 0)
6721                 {
6722                 destPath.append(outName);
6723                 }
6724             else
6725                 {
6726                 destPath.append(fileName);
6727                 destPath[destPath.size()-2] = 'm';
6728                 }
6730             String fullDest = parent.resolve(destPath);
6732             if (!isNewerThan(fullSource, fullDest))
6733                 {
6734                 //trace("skip %s", fullSource.c_str());
6735                 continue;
6736                 }
6737                 
6738             String cmd = command;
6739             cmd.append(" ");
6740             cmd.append(fullSource);
6741             cmd.append(" -o ");
6742             cmd.append(fullDest);
6743             
6744             int pos = fullDest.find_last_of('/');
6745             if (pos>0)
6746                 {
6747                 String fullDestPath = fullDest.substr(0, pos);
6748                 if (!createDirectory(fullDestPath))
6749                     return false;
6750                 }
6754             String outString, errString;
6755             if (!executeCommand(cmd.c_str(), "", outString, errString))
6756                 {
6757                 error("<msgfmt> problem: %s", errString.c_str());
6758                 return false;
6759                 }
6760             }
6762         return true;
6763         }
6765     virtual bool parse(Element *elem)
6766         {
6767         String s;
6768         if (!parent.getAttribute(elem, "command", s))
6769             return false;
6770         if (s.size()>0)
6771             command = s;
6772         if (!parent.getAttribute(elem, "todir", toDirName))
6773             return false;
6774         if (!parent.getAttribute(elem, "out", outName))
6775             return false;
6776         if (!parent.getAttribute(elem, "owndir", s))
6777             return false;
6778         if (s.size()>0 && !getBool(s, owndir))
6779             return false;
6780             
6781         std::vector<Element *> children = elem->getChildren();
6782         for (unsigned int i=0 ; i<children.size() ; i++)
6783             {
6784             Element *child = children[i];
6785             String tagName = child->getName();
6786             if (tagName == "fileset")
6787                 {
6788                 if (!parseFileSet(child, parent, fileSet))
6789                     return false;
6790                 }
6791             }
6792         return true;
6793         }
6795 private:
6797     String  command;
6798     String  toDirName;
6799     String  outName;
6800     FileSet fileSet;
6801     bool    owndir;
6803 };
6809 /**
6810  *  Process an archive to allow random access
6811  */
6812 class TaskRanlib : public Task
6814 public:
6816     TaskRanlib(MakeBase &par) : Task(par)
6817         {
6818         type = TASK_RANLIB; name = "ranlib";
6819         command = "ranlib";
6820         }
6822     virtual ~TaskRanlib()
6823         {}
6825     virtual bool execute()
6826         {
6827         String fullName = parent.resolve(fileName);
6828         //trace("fullDir:%s", fullDir.c_str());
6829         String cmd = command;
6830         cmd.append(" ");
6831         cmd.append(fullName);
6832         String outbuf, errbuf;
6833         if (!executeCommand(cmd, "", outbuf, errbuf))
6834             return false;
6835         return true;
6836         }
6838     virtual bool parse(Element *elem)
6839         {
6840         String s;
6841         if (!parent.getAttribute(elem, "command", s))
6842             return false;
6843         if (s.size()>0)
6844            command = s;
6845         if (!parent.getAttribute(elem, "file", fileName))
6846             return false;
6847         if (fileName.size() == 0)
6848             {
6849             error("<ranlib> requires 'file=\"fileNname\"' attribute");
6850             return false;
6851             }
6852         return true;
6853         }
6855 private:
6857     String fileName;
6858     String command;
6859 };
6863 /**
6864  * Run the "ar" command to archive .o's into a .a
6865  */
6866 class TaskRC : public Task
6868 public:
6870     TaskRC(MakeBase &par) : Task(par)
6871         {
6872         type = TASK_RC; name = "rc";
6873         command = "windres";
6874         }
6876     virtual ~TaskRC()
6877         {}
6879     virtual bool execute()
6880         {
6881         String fullFile = parent.resolve(fileName);
6882         String fullOut  = parent.resolve(outName);
6883         if (!isNewerThan(fullFile, fullOut))
6884             return true;
6885         String cmd = command;
6886         cmd.append(" -o ");
6887         cmd.append(fullOut);
6888         cmd.append(" ");
6889         cmd.append(flags);
6890         cmd.append(" ");
6891         cmd.append(fullFile);
6893         String outString, errString;
6894         if (!executeCommand(cmd.c_str(), "", outString, errString))
6895             {
6896             error("RC problem: %s", errString.c_str());
6897             return false;
6898             }
6899         return true;
6900         }
6902     virtual bool parse(Element *elem)
6903         {
6904         if (!parent.getAttribute(elem, "command", command))
6905             return false;
6906         if (!parent.getAttribute(elem, "file", fileName))
6907             return false;
6908         if (!parent.getAttribute(elem, "out", outName))
6909             return false;
6910         std::vector<Element *> children = elem->getChildren();
6911         for (unsigned int i=0 ; i<children.size() ; i++)
6912             {
6913             Element *child = children[i];
6914             String tagName = child->getName();
6915             if (tagName == "flags")
6916                 {
6917                 if (!parent.getValue(child, flags))
6918                     return false;
6919                 }
6920             }
6921         return true;
6922         }
6924 private:
6926     String command;
6927     String flags;
6928     String fileName;
6929     String outName;
6931 };
6935 /**
6936  *  Collect .o's into a .so or DLL
6937  */
6938 class TaskSharedLib : public Task
6940 public:
6942     TaskSharedLib(MakeBase &par) : Task(par)
6943         {
6944         type = TASK_SHAREDLIB; name = "dll";
6945         command = "ar crv";
6946         }
6948     virtual ~TaskSharedLib()
6949         {}
6951     virtual bool execute()
6952         {
6953         //trace("###########HERE %d", fileSet.size());
6954         bool doit = false;
6955         
6956         String fullOut = parent.resolve(fileName);
6957         //trace("ar fullout: %s", fullOut.c_str());
6958         
6959         if (!listFiles(parent, fileSet))
6960             return false;
6961         String fileSetDir = fileSet.getDirectory();
6963         for (unsigned int i=0 ; i<fileSet.size() ; i++)
6964             {
6965             String fname;
6966             if (fileSetDir.size()>0)
6967                 {
6968                 fname.append(fileSetDir);
6969                 fname.append("/");
6970                 }
6971             fname.append(fileSet[i]);
6972             String fullName = parent.resolve(fname);
6973             //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
6974             if (isNewerThan(fullName, fullOut))
6975                 doit = true;
6976             }
6977         //trace("Needs it:%d", doit);
6978         if (!doit)
6979             {
6980             return true;
6981             }
6983         String cmd = "dllwrap";
6984         cmd.append(" -o ");
6985         cmd.append(fullOut);
6986         if (defFileName.size()>0)
6987             {
6988             cmd.append(" --def ");
6989             cmd.append(defFileName);
6990             cmd.append(" ");
6991             }
6992         if (impFileName.size()>0)
6993             {
6994             cmd.append(" --implib ");
6995             cmd.append(impFileName);
6996             cmd.append(" ");
6997             }
6998         for (unsigned int i=0 ; i<fileSet.size() ; i++)
6999             {
7000             String fname;
7001             if (fileSetDir.size()>0)
7002                 {
7003                 fname.append(fileSetDir);
7004                 fname.append("/");
7005                 }
7006             fname.append(fileSet[i]);
7007             String fullName = parent.resolve(fname);
7009             cmd.append(" ");
7010             cmd.append(fullName);
7011             }
7012         cmd.append(" ");
7013         cmd.append(libs);
7015         String outString, errString;
7016         if (!executeCommand(cmd.c_str(), "", outString, errString))
7017             {
7018             error("<sharedlib> problem: %s", errString.c_str());
7019             return false;
7020             }
7022         return true;
7023         }
7025     virtual bool parse(Element *elem)
7026         {
7027         if (!parent.getAttribute(elem, "file", fileName))
7028             return false;
7029         if (!parent.getAttribute(elem, "import", impFileName))
7030             return false;
7031         if (!parent.getAttribute(elem, "def", defFileName))
7032             return false;
7033             
7034         std::vector<Element *> children = elem->getChildren();
7035         for (unsigned int i=0 ; i<children.size() ; i++)
7036             {
7037             Element *child = children[i];
7038             String tagName = child->getName();
7039             if (tagName == "fileset")
7040                 {
7041                 if (!parseFileSet(child, parent, fileSet))
7042                     return false;
7043                 }
7044             else if (tagName == "libs")
7045                 {
7046                 if (!parent.getValue(child, libs))
7047                     return false;
7048                 libs = strip(libs);
7049                 }
7050             }
7051         return true;
7052         }
7054 private:
7056     String command;
7057     String fileName;
7058     String defFileName;
7059     String impFileName;
7060     FileSet fileSet;
7061     String libs;
7063 };
7066 /**
7067  * Run the "ar" command to archive .o's into a .a
7068  */
7069 class TaskStaticLib : public Task
7071 public:
7073     TaskStaticLib(MakeBase &par) : Task(par)
7074         {
7075         type = TASK_STATICLIB; name = "staticlib";
7076         command = "ar crv";
7077         }
7079     virtual ~TaskStaticLib()
7080         {}
7082     virtual bool execute()
7083         {
7084         //trace("###########HERE %d", fileSet.size());
7085         bool doit = false;
7086         
7087         String fullOut = parent.resolve(fileName);
7088         //trace("ar fullout: %s", fullOut.c_str());
7089         
7090         if (!listFiles(parent, fileSet))
7091             return false;
7092         String fileSetDir = fileSet.getDirectory();
7094         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7095             {
7096             String fname;
7097             if (fileSetDir.size()>0)
7098                 {
7099                 fname.append(fileSetDir);
7100                 fname.append("/");
7101                 }
7102             fname.append(fileSet[i]);
7103             String fullName = parent.resolve(fname);
7104             //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
7105             if (isNewerThan(fullName, fullOut))
7106                 doit = true;
7107             }
7108         //trace("Needs it:%d", doit);
7109         if (!doit)
7110             {
7111             return true;
7112             }
7114         String cmd = command;
7115         cmd.append(" ");
7116         cmd.append(fullOut);
7117         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7118             {
7119             String fname;
7120             if (fileSetDir.size()>0)
7121                 {
7122                 fname.append(fileSetDir);
7123                 fname.append("/");
7124                 }
7125             fname.append(fileSet[i]);
7126             String fullName = parent.resolve(fname);
7128             cmd.append(" ");
7129             cmd.append(fullName);
7130             }
7132         String outString, errString;
7133         if (!executeCommand(cmd.c_str(), "", outString, errString))
7134             {
7135             error("<staticlib> problem: %s", errString.c_str());
7136             return false;
7137             }
7139         return true;
7140         }
7142     virtual bool parse(Element *elem)
7143         {
7144         String s;
7145         if (!parent.getAttribute(elem, "command", s))
7146             return false;
7147         if (s.size()>0)
7148             command = s;
7149         if (!parent.getAttribute(elem, "file", fileName))
7150             return false;
7151             
7152         std::vector<Element *> children = elem->getChildren();
7153         for (unsigned int i=0 ; i<children.size() ; i++)
7154             {
7155             Element *child = children[i];
7156             String tagName = child->getName();
7157             if (tagName == "fileset")
7158                 {
7159                 if (!parseFileSet(child, parent, fileSet))
7160                     return false;
7161                 }
7162             }
7163         return true;
7164         }
7166 private:
7168     String command;
7169     String fileName;
7170     FileSet fileSet;
7172 };
7175 /**
7176  * Strip an executable
7177  */
7178 class TaskStrip : public Task
7180 public:
7182     TaskStrip(MakeBase &par) : Task(par)
7183         { type = TASK_STRIP; name = "strip"; }
7185     virtual ~TaskStrip()
7186         {}
7188     virtual bool execute()
7189         {
7190         String fullName = parent.resolve(fileName);
7191         //trace("fullDir:%s", fullDir.c_str());
7192         String cmd;
7193         String outbuf, errbuf;
7195         if (symFileName.size()>0)
7196             {
7197             String symFullName = parent.resolve(symFileName);
7198             cmd = "objcopy --only-keep-debug ";
7199             cmd.append(getNativePath(fullName));
7200             cmd.append(" ");
7201             cmd.append(getNativePath(symFullName));
7202             if (!executeCommand(cmd, "", outbuf, errbuf))
7203                 {
7204                 error("<strip> symbol file failed : %s", errbuf.c_str());
7205                 return false;
7206                 }
7207             }
7208             
7209         cmd = "strip ";
7210         cmd.append(getNativePath(fullName));
7211         if (!executeCommand(cmd, "", outbuf, errbuf))
7212             {
7213             error("<strip> failed : %s", errbuf.c_str());
7214             return false;
7215             }
7216         return true;
7217         }
7219     virtual bool parse(Element *elem)
7220         {
7221         if (!parent.getAttribute(elem, "file", fileName))
7222             return false;
7223         if (!parent.getAttribute(elem, "symfile", symFileName))
7224             return false;
7225         if (fileName.size() == 0)
7226             {
7227             error("<strip> requires 'file=\"fileName\"' attribute");
7228             return false;
7229             }
7230         return true;
7231         }
7233 private:
7235     String fileName;
7236     String symFileName;
7237 };
7240 /**
7241  *
7242  */
7243 class TaskTstamp : public Task
7245 public:
7247     TaskTstamp(MakeBase &par) : Task(par)
7248         { type = TASK_TSTAMP; name = "tstamp"; }
7250     virtual ~TaskTstamp()
7251         {}
7253     virtual bool execute()
7254         {
7255         return true;
7256         }
7258     virtual bool parse(Element *elem)
7259         {
7260         //trace("tstamp parse");
7261         return true;
7262         }
7263 };
7267 /**
7268  *
7269  */
7270 Task *Task::createTask(Element *elem)
7272     String tagName = elem->getName();
7273     //trace("task:%s", tagName.c_str());
7274     Task *task = NULL;
7275     if (tagName == "cc")
7276         task = new TaskCC(parent);
7277     else if (tagName == "copy")
7278         task = new TaskCopy(parent);
7279     else if (tagName == "delete")
7280         task = new TaskDelete(parent);
7281     else if (tagName == "jar")
7282         task = new TaskJar(parent);
7283     else if (tagName == "javac")
7284         task = new TaskJavac(parent);
7285     else if (tagName == "link")
7286         task = new TaskLink(parent);
7287     else if (tagName == "makefile")
7288         task = new TaskMakeFile(parent);
7289     else if (tagName == "mkdir")
7290         task = new TaskMkDir(parent);
7291     else if (tagName == "msgfmt")
7292         task = new TaskMsgFmt(parent);
7293     else if (tagName == "ranlib")
7294         task = new TaskRanlib(parent);
7295     else if (tagName == "rc")
7296         task = new TaskRC(parent);
7297     else if (tagName == "sharedlib")
7298         task = new TaskSharedLib(parent);
7299     else if (tagName == "staticlib")
7300         task = new TaskStaticLib(parent);
7301     else if (tagName == "strip")
7302         task = new TaskStrip(parent);
7303     else if (tagName == "tstamp")
7304         task = new TaskTstamp(parent);
7305     else
7306         {
7307         error("Unknown task '%s'", tagName.c_str());
7308         return NULL;
7309         }
7311     if (!task->parse(elem))
7312         {
7313         delete task;
7314         return NULL;
7315         }
7316     return task;
7321 //########################################################################
7322 //# T A R G E T
7323 //########################################################################
7325 /**
7326  *
7327  */
7328 class Target : public MakeBase
7331 public:
7333     /**
7334      *
7335      */
7336     Target(Make &par) : parent(par)
7337         { init(); }
7339     /**
7340      *
7341      */
7342     Target(const Target &other) : parent(other.parent)
7343         { init(); assign(other); }
7345     /**
7346      *
7347      */
7348     Target &operator=(const Target &other)
7349         { init(); assign(other); return *this; }
7351     /**
7352      *
7353      */
7354     virtual ~Target()
7355         { cleanup() ; }
7358     /**
7359      *
7360      */
7361     virtual Make &getParent()
7362         { return parent; }
7364     /**
7365      *
7366      */
7367     virtual String getName()
7368         { return name; }
7370     /**
7371      *
7372      */
7373     virtual void setName(const String &val)
7374         { name = val; }
7376     /**
7377      *
7378      */
7379     virtual String getDescription()
7380         { return description; }
7382     /**
7383      *
7384      */
7385     virtual void setDescription(const String &val)
7386         { description = val; }
7388     /**
7389      *
7390      */
7391     virtual void addDependency(const String &val)
7392         { deps.push_back(val); }
7394     /**
7395      *
7396      */
7397     virtual void parseDependencies(const String &val)
7398         { deps = tokenize(val, ", "); }
7400     /**
7401      *
7402      */
7403     virtual std::vector<String> &getDependencies()
7404         { return deps; }
7406     /**
7407      *
7408      */
7409     virtual String getIf()
7410         { return ifVar; }
7412     /**
7413      *
7414      */
7415     virtual void setIf(const String &val)
7416         { ifVar = val; }
7418     /**
7419      *
7420      */
7421     virtual String getUnless()
7422         { return unlessVar; }
7424     /**
7425      *
7426      */
7427     virtual void setUnless(const String &val)
7428         { unlessVar = val; }
7430     /**
7431      *
7432      */
7433     virtual void addTask(Task *val)
7434         { tasks.push_back(val); }
7436     /**
7437      *
7438      */
7439     virtual std::vector<Task *> &getTasks()
7440         { return tasks; }
7442 private:
7444     void init()
7445         {
7446         }
7448     void cleanup()
7449         {
7450         tasks.clear();
7451         }
7453     void assign(const Target &other)
7454         {
7455         //parent      = other.parent;
7456         name        = other.name;
7457         description = other.description;
7458         ifVar       = other.ifVar;
7459         unlessVar   = other.unlessVar;
7460         deps        = other.deps;
7461         tasks       = other.tasks;
7462         }
7464     Make &parent;
7466     String name;
7468     String description;
7470     String ifVar;
7472     String unlessVar;
7474     std::vector<String> deps;
7476     std::vector<Task *> tasks;
7478 };
7487 //########################################################################
7488 //# M A K E
7489 //########################################################################
7492 /**
7493  *
7494  */
7495 class Make : public MakeBase
7498 public:
7500     /**
7501      *
7502      */
7503     Make()
7504         { init(); }
7506     /**
7507      *
7508      */
7509     Make(const Make &other)
7510         { assign(other); }
7512     /**
7513      *
7514      */
7515     Make &operator=(const Make &other)
7516         { assign(other); return *this; }
7518     /**
7519      *
7520      */
7521     virtual ~Make()
7522         { cleanup(); }
7524     /**
7525      *
7526      */
7527     virtual std::map<String, Target> &getTargets()
7528         { return targets; }
7531     /**
7532      *
7533      */
7534     virtual String version()
7535         { return BUILDTOOL_VERSION; }
7537     /**
7538      * Overload a <property>
7539      */
7540     virtual bool specifyProperty(const String &name,
7541                                  const String &value);
7543     /**
7544      *
7545      */
7546     virtual bool run();
7548     /**
7549      *
7550      */
7551     virtual bool run(const String &target);
7555 private:
7557     /**
7558      *
7559      */
7560     void init();
7562     /**
7563      *
7564      */
7565     void cleanup();
7567     /**
7568      *
7569      */
7570     void assign(const Make &other);
7572     /**
7573      *
7574      */
7575     bool executeTask(Task &task);
7578     /**
7579      *
7580      */
7581     bool executeTarget(Target &target,
7582              std::set<String> &targetsCompleted);
7585     /**
7586      *
7587      */
7588     bool execute();
7590     /**
7591      *
7592      */
7593     bool checkTargetDependencies(Target &prop,
7594                     std::vector<String> &depList);
7596     /**
7597      *
7598      */
7599     bool parsePropertyFile(const String &fileName,
7600                            const String &prefix);
7602     /**
7603      *
7604      */
7605     bool parseProperty(Element *elem);
7607     /**
7608      *
7609      */
7610     bool parseTask(Task &task, Element *elem);
7612     /**
7613      *
7614      */
7615     bool parseFile();
7617     /**
7618      *
7619      */
7620     std::vector<String> glob(const String &pattern);
7623     //###############
7624     //# Fields
7625     //###############
7627     String projectName;
7629     String currentTarget;
7631     String defaultTarget;
7633     String specifiedTarget;
7635     String baseDir;
7637     String description;
7638     
7639     String envAlias;
7641     //std::vector<Property> properties;
7642     
7643     std::map<String, Target> targets;
7645     std::vector<Task *> allTasks;
7646     
7647     std::map<String, String> specifiedProperties;
7649 };
7652 //########################################################################
7653 //# C L A S S  M A I N T E N A N C E
7654 //########################################################################
7656 /**
7657  *
7658  */
7659 void Make::init()
7661     uri             = "build.xml";
7662     projectName     = "";
7663     currentTarget   = "";
7664     defaultTarget   = "";
7665     specifiedTarget = "";
7666     baseDir         = "";
7667     description     = "";
7668     envAlias        = "";
7669     properties.clear();
7670     for (unsigned int i = 0 ; i < allTasks.size() ; i++)
7671         delete allTasks[i];
7672     allTasks.clear();
7677 /**
7678  *
7679  */
7680 void Make::cleanup()
7682     for (unsigned int i = 0 ; i < allTasks.size() ; i++)
7683         delete allTasks[i];
7684     allTasks.clear();
7689 /**
7690  *
7691  */
7692 void Make::assign(const Make &other)
7694     uri              = other.uri;
7695     projectName      = other.projectName;
7696     currentTarget    = other.currentTarget;
7697     defaultTarget    = other.defaultTarget;
7698     specifiedTarget  = other.specifiedTarget;
7699     baseDir          = other.baseDir;
7700     description      = other.description;
7701     properties       = other.properties;
7706 //########################################################################
7707 //# U T I L I T Y    T A S K S
7708 //########################################################################
7710 /**
7711  *  Perform a file globbing
7712  */
7713 std::vector<String> Make::glob(const String &pattern)
7715     std::vector<String> res;
7716     return res;
7720 //########################################################################
7721 //# P U B L I C    A P I
7722 //########################################################################
7726 /**
7727  *
7728  */
7729 bool Make::executeTarget(Target &target,
7730              std::set<String> &targetsCompleted)
7733     String name = target.getName();
7735     //First get any dependencies for this target
7736     std::vector<String> deps = target.getDependencies();
7737     for (unsigned int i=0 ; i<deps.size() ; i++)
7738         {
7739         String dep = deps[i];
7740         //Did we do it already?  Skip
7741         if (targetsCompleted.find(dep)!=targetsCompleted.end())
7742             continue;
7743             
7744         std::map<String, Target> &tgts =
7745                target.getParent().getTargets();
7746         std::map<String, Target>::iterator iter =
7747                tgts.find(dep);
7748         if (iter == tgts.end())
7749             {
7750             error("Target '%s' dependency '%s' not found",
7751                       name.c_str(),  dep.c_str());
7752             return false;
7753             }
7754         Target depTarget = iter->second;
7755         if (!executeTarget(depTarget, targetsCompleted))
7756             {
7757             return false;
7758             }
7759         }
7761     status("## Target : %s", name.c_str());
7763     //Now let's do the tasks
7764     std::vector<Task *> &tasks = target.getTasks();
7765     for (unsigned int i=0 ; i<tasks.size() ; i++)
7766         {
7767         Task *task = tasks[i];
7768         status("---- task : %s", task->getName().c_str());
7769         if (!task->execute())
7770             {
7771             return false;
7772             }
7773         }
7774         
7775     targetsCompleted.insert(name);
7776     
7777     return true;
7782 /**
7783  *  Main execute() method.  Start here and work
7784  *  up the dependency tree 
7785  */
7786 bool Make::execute()
7788     status("######## EXECUTE");
7790     //Determine initial target
7791     if (specifiedTarget.size()>0)
7792         {
7793         currentTarget = specifiedTarget;
7794         }
7795     else if (defaultTarget.size()>0)
7796         {
7797         currentTarget = defaultTarget;
7798         }
7799     else
7800         {
7801         error("execute: no specified or default target requested");
7802         return false;
7803         }
7805     std::map<String, Target>::iterator iter =
7806                targets.find(currentTarget);
7807     if (iter == targets.end())
7808         {
7809         error("Initial target '%s' not found",
7810                  currentTarget.c_str());
7811         return false;
7812         }
7813         
7814     //Now run
7815     Target target = iter->second;
7816     std::set<String> targetsCompleted;
7817     if (!executeTarget(target, targetsCompleted))
7818         {
7819         return false;
7820         }
7822     status("######## EXECUTE COMPLETE");
7823     return true;
7829 /**
7830  *
7831  */
7832 bool Make::checkTargetDependencies(Target &target, 
7833                             std::vector<String> &depList)
7835     String tgtName = target.getName().c_str();
7836     depList.push_back(tgtName);
7838     std::vector<String> deps = target.getDependencies();
7839     for (unsigned int i=0 ; i<deps.size() ; i++)
7840         {
7841         String dep = deps[i];
7842         //First thing entered was the starting Target
7843         if (dep == depList[0])
7844             {
7845             error("Circular dependency '%s' found at '%s'",
7846                       dep.c_str(), tgtName.c_str());
7847             std::vector<String>::iterator diter;
7848             for (diter=depList.begin() ; diter!=depList.end() ; diter++)
7849                 {
7850                 error("  %s", diter->c_str());
7851                 }
7852             return false;
7853             }
7855         std::map<String, Target> &tgts =
7856                   target.getParent().getTargets();
7857         std::map<String, Target>::iterator titer = tgts.find(dep);
7858         if (titer == tgts.end())
7859             {
7860             error("Target '%s' dependency '%s' not found",
7861                       tgtName.c_str(), dep.c_str());
7862             return false;
7863             }
7864         if (!checkTargetDependencies(titer->second, depList))
7865             {
7866             return false;
7867             }
7868         }
7869     return true;
7876 static int getword(int pos, const String &inbuf, String &result)
7878     int p = pos;
7879     int len = (int)inbuf.size();
7880     String val;
7881     while (p < len)
7882         {
7883         char ch = inbuf[p];
7884         if (!isalnum(ch) && ch!='.' && ch!='_')
7885             break;
7886         val.push_back(ch);
7887         p++;
7888         }
7889     result = val;
7890     return p;
7896 /**
7897  *
7898  */
7899 bool Make::parsePropertyFile(const String &fileName,
7900                              const String &prefix)
7902     FILE *f = fopen(fileName.c_str(), "r");
7903     if (!f)
7904         {
7905         error("could not open property file %s", fileName.c_str());
7906         return false;
7907         }
7908     int linenr = 0;
7909     while (!feof(f))
7910         {
7911         char buf[256];
7912         if (!fgets(buf, 255, f))
7913             break;
7914         linenr++;
7915         String s = buf;
7916         s = trim(s);
7917         int len = s.size();
7918         if (len == 0)
7919             continue;
7920         if (s[0] == '#')
7921             continue;
7922         String key;
7923         String val;
7924         int p = 0;
7925         int p2 = getword(p, s, key);
7926         if (p2 <= p)
7927             {
7928             error("property file %s, line %d: expected keyword",
7929                     fileName.c_str(), linenr);
7930             return false;
7931             }
7932         if (prefix.size() > 0)
7933             {
7934             key.insert(0, prefix);
7935             }
7937         //skip whitespace
7938         for (p=p2 ; p<len ; p++)
7939             if (!isspace(s[p]))
7940                 break;
7942         if (p>=len || s[p]!='=')
7943             {
7944             error("property file %s, line %d: expected '='",
7945                     fileName.c_str(), linenr);
7946             return false;
7947             }
7948         p++;
7950         //skip whitespace
7951         for ( ; p<len ; p++)
7952             if (!isspace(s[p]))
7953                 break;
7955         /* This way expects a word after the =
7956         p2 = getword(p, s, val);
7957         if (p2 <= p)
7958             {
7959             error("property file %s, line %d: expected value",
7960                     fileName.c_str(), linenr);
7961             return false;
7962             }
7963         */
7964         // This way gets the rest of the line after the =
7965         if (p>=len)
7966             {
7967             error("property file %s, line %d: expected value",
7968                     fileName.c_str(), linenr);
7969             return false;
7970             }
7971         val = s.substr(p);
7972         if (key.size()==0)
7973             continue;
7974         //allow property to be set, even if val=""
7976         //trace("key:'%s' val:'%s'", key.c_str(), val.c_str());
7977         //See if we wanted to overload this property
7978         std::map<String, String>::iterator iter =
7979             specifiedProperties.find(key);
7980         if (iter!=specifiedProperties.end())
7981             {
7982             val = iter->second;
7983             status("overloading property '%s' = '%s'",
7984                    key.c_str(), val.c_str());
7985             }
7986         properties[key] = val;
7987         }
7988     fclose(f);
7989     return true;
7995 /**
7996  *
7997  */
7998 bool Make::parseProperty(Element *elem)
8000     std::vector<Attribute> &attrs = elem->getAttributes();
8001     for (unsigned int i=0 ; i<attrs.size() ; i++)
8002         {
8003         String attrName = attrs[i].getName();
8004         String attrVal  = attrs[i].getValue();
8006         if (attrName == "name")
8007             {
8008             String val;
8009             if (!getAttribute(elem, "value", val))
8010                 return false;
8011             if (val.size() > 0)
8012                 {
8013                 properties[attrVal] = val;
8014                 }
8015             else
8016                 {
8017                 if (!getAttribute(elem, "location", val))
8018                     return false;
8019                 //let the property exist, even if not defined
8020                 properties[attrVal] = val;
8021                 }
8022             //See if we wanted to overload this property
8023             std::map<String, String>::iterator iter =
8024                 specifiedProperties.find(attrVal);
8025             if (iter != specifiedProperties.end())
8026                 {
8027                 val = iter->second;
8028                 status("overloading property '%s' = '%s'",
8029                     attrVal.c_str(), val.c_str());
8030                 properties[attrVal] = val;
8031                 }
8032             }
8033         else if (attrName == "file")
8034             {
8035             String prefix;
8036             if (!getAttribute(elem, "prefix", prefix))
8037                 return false;
8038             if (prefix.size() > 0)
8039                 {
8040                 if (prefix[prefix.size()-1] != '.')
8041                     prefix.push_back('.');
8042                 }
8043             if (!parsePropertyFile(attrName, prefix))
8044                 return false;
8045             }
8046         else if (attrName == "environment")
8047             {
8048             if (envAlias.size() > 0)
8049                 {
8050                 error("environment property can only be set once");
8051                 return false;
8052                 }
8053             envAlias = attrVal;
8054             }
8055         }
8057     return true;
8063 /**
8064  *
8065  */
8066 bool Make::parseFile()
8068     status("######## PARSE : %s", uri.getPath().c_str());
8070     Parser parser;
8071     Element *root = parser.parseFile(uri.getNativePath());
8072     if (!root)
8073         {
8074         error("Could not open %s for reading",
8075               uri.getNativePath().c_str());
8076         return false;
8077         }
8079     if (root->getChildren().size()==0 ||
8080         root->getChildren()[0]->getName()!="project")
8081         {
8082         error("Main xml element should be <project>");
8083         delete root;
8084         return false;
8085         }
8087     //########## Project attributes
8088     Element *project = root->getChildren()[0];
8089     String s = project->getAttribute("name");
8090     if (s.size() > 0)
8091         projectName = s;
8092     s = project->getAttribute("default");
8093     if (s.size() > 0)
8094         defaultTarget = s;
8095     s = project->getAttribute("basedir");
8096     if (s.size() > 0)
8097         baseDir = s;
8099     //######### PARSE MEMBERS
8100     std::vector<Element *> children = project->getChildren();
8101     for (unsigned int i=0 ; i<children.size() ; i++)
8102         {
8103         Element *elem = children[i];
8104         String tagName = elem->getName();
8106         //########## DESCRIPTION
8107         if (tagName == "description")
8108             {
8109             description = parser.trim(elem->getValue());
8110             }
8112         //######### PROPERTY
8113         else if (tagName == "property")
8114             {
8115             if (!parseProperty(elem))
8116                 return false;
8117             }
8119         //######### TARGET
8120         else if (tagName == "target")
8121             {
8122             String tname   = elem->getAttribute("name");
8123             String tdesc   = elem->getAttribute("description");
8124             String tdeps   = elem->getAttribute("depends");
8125             String tif     = elem->getAttribute("if");
8126             String tunless = elem->getAttribute("unless");
8127             Target target(*this);
8128             target.setName(tname);
8129             target.setDescription(tdesc);
8130             target.parseDependencies(tdeps);
8131             target.setIf(tif);
8132             target.setUnless(tunless);
8133             std::vector<Element *> telems = elem->getChildren();
8134             for (unsigned int i=0 ; i<telems.size() ; i++)
8135                 {
8136                 Element *telem = telems[i];
8137                 Task breeder(*this);
8138                 Task *task = breeder.createTask(telem);
8139                 if (!task)
8140                     return false;
8141                 allTasks.push_back(task);
8142                 target.addTask(task);
8143                 }
8145             //Check name
8146             if (tname.size() == 0)
8147                 {
8148                 error("no name for target");
8149                 return false;
8150                 }
8151             //Check for duplicate name
8152             if (targets.find(tname) != targets.end())
8153                 {
8154                 error("target '%s' already defined", tname.c_str());
8155                 return false;
8156                 }
8157             //more work than targets[tname]=target, but avoids default allocator
8158             targets.insert(std::make_pair<String, Target>(tname, target));
8159             }
8161         }
8163     std::map<String, Target>::iterator iter;
8164     for (iter = targets.begin() ; iter!= targets.end() ; iter++)
8165         {
8166         Target tgt = iter->second;
8167         std::vector<String> depList;
8168         if (!checkTargetDependencies(tgt, depList))
8169             {
8170             return false;
8171             }
8172         }
8175     delete root;
8176     status("######## PARSE COMPLETE");
8177     return true;
8181 /**
8182  * Overload a <property>
8183  */
8184 bool Make::specifyProperty(const String &name, const String &value)
8186     if (specifiedProperties.find(name) != specifiedProperties.end())
8187         {
8188         error("Property %s already specified", name.c_str());
8189         return false;
8190         }
8191     specifiedProperties[name] = value;
8192     return true;
8197 /**
8198  *
8199  */
8200 bool Make::run()
8202     if (!parseFile())
8203         return false;
8204         
8205     if (!execute())
8206         return false;
8208     return true;
8214 /**
8215  * Get a formatted MM:SS.sss time elapsed string
8216  */ 
8217 static String
8218 timeDiffString(struct timeval &x, struct timeval &y)
8220     long microsX  = x.tv_usec;
8221     long secondsX = x.tv_sec;
8222     long microsY  = y.tv_usec;
8223     long secondsY = y.tv_sec;
8224     if (microsX < microsY)
8225         {
8226         microsX += 1000000;
8227         secondsX -= 1;
8228         }
8230     int seconds = (int)(secondsX - secondsY);
8231     int millis  = (int)((microsX - microsY)/1000);
8233     int minutes = seconds/60;
8234     seconds -= minutes*60;
8235     char buf[80];
8236     snprintf(buf, 79, "%dm %d.%03ds", minutes, seconds, millis);
8237     String ret = buf;
8238     return ret;
8239     
8242 /**
8243  *
8244  */
8245 bool Make::run(const String &target)
8247     status("####################################################");
8248     status("#   %s", version().c_str());
8249     status("####################################################");
8250     struct timeval timeStart, timeEnd;
8251     ::gettimeofday(&timeStart, NULL);
8252     specifiedTarget = target;
8253     if (!run())
8254         return false;
8255     ::gettimeofday(&timeEnd, NULL);
8256     String timeStr = timeDiffString(timeEnd, timeStart);
8257     status("####################################################");
8258     status("#   BuildTool Completed : %s", timeStr.c_str());
8259     status("####################################################");
8260     return true;
8269 }// namespace buildtool
8270 //########################################################################
8271 //# M A I N
8272 //########################################################################
8274 typedef buildtool::String String;
8276 /**
8277  *  Format an error message in printf() style
8278  */
8279 static void error(char *fmt, ...)
8281     va_list ap;
8282     va_start(ap, fmt);
8283     fprintf(stderr, "BuildTool error: ");
8284     vfprintf(stderr, fmt, ap);
8285     fprintf(stderr, "\n");
8286     va_end(ap);
8290 static bool parseProperty(const String &s, String &name, String &val)
8292     int len = s.size();
8293     int i;
8294     for (i=0 ; i<len ; i++)
8295         {
8296         char ch = s[i];
8297         if (ch == '=')
8298             break;
8299         name.push_back(ch);
8300         }
8301     if (i>=len || s[i]!='=')
8302         {
8303         error("property requires -Dname=value");
8304         return false;
8305         }
8306     i++;
8307     for ( ; i<len ; i++)
8308         {
8309         char ch = s[i];
8310         val.push_back(ch);
8311         }
8312     return true;
8316 /**
8317  * Compare a buffer with a key, for the length of the key
8318  */
8319 static bool sequ(const String &buf, char *key)
8321     int len = buf.size();
8322     for (int i=0 ; key[i] && i<len ; i++)
8323         {
8324         if (key[i] != buf[i])
8325             return false;
8326         }        
8327     return true;
8330 static void usage(int argc, char **argv)
8332     printf("usage:\n");
8333     printf("   %s [options] [target]\n", argv[0]);
8334     printf("Options:\n");
8335     printf("  -help, -h              print this message\n");
8336     printf("  -version               print the version information and exit\n");
8337     printf("  -file <file>           use given buildfile\n");
8338     printf("  -f <file>                 ''\n");
8339     printf("  -D<property>=<value>   use value for given property\n");
8345 /**
8346  * Parse the command-line args, get our options,
8347  * and run this thing
8348  */   
8349 static bool parseOptions(int argc, char **argv)
8351     if (argc < 1)
8352         {
8353         error("Cannot parse arguments");
8354         return false;
8355         }
8357     buildtool::Make make;
8359     String target;
8361     //char *progName = argv[0];
8362     for (int i=1 ; i<argc ; i++)
8363         {
8364         String arg = argv[i];
8365         if (arg.size()>1 && arg[0]=='-')
8366             {
8367             if (arg == "-h" || arg == "-help")
8368                 {
8369                 usage(argc,argv);
8370                 return true;
8371                 }
8372             else if (arg == "-version")
8373                 {
8374                 printf("%s", make.version().c_str());
8375                 return true;
8376                 }
8377             else if (arg == "-f" || arg == "-file")
8378                 {
8379                 if (i>=argc)
8380                    {
8381                    usage(argc, argv);
8382                    return false;
8383                    }
8384                 i++; //eat option
8385                 make.setURI(argv[i]);
8386                 }
8387             else if (arg.size()>2 && sequ(arg, "-D"))
8388                 {
8389                 String s = arg.substr(2, s.size());
8390                 String name, value;
8391                 if (!parseProperty(s, name, value))
8392                    {
8393                    usage(argc, argv);
8394                    return false;
8395                    }
8396                 if (!make.specifyProperty(name, value))
8397                     return false;
8398                 }
8399             else
8400                 {
8401                 error("Unknown option:%s", arg.c_str());
8402                 return false;
8403                 }
8404             }
8405         else
8406             {
8407             if (target.size()>0)
8408                 {
8409                 error("only one initial target");
8410                 usage(argc, argv);
8411                 return false;
8412                 }
8413             target = arg;
8414             }
8415         }
8417     //We have the options.  Now execute them
8418     if (!make.run(target))
8419         return false;
8421     return true;
8427 /*
8428 static bool runMake()
8430     buildtool::Make make;
8431     if (!make.run())
8432         return false;
8433     return true;
8437 static bool pkgConfigTest()
8439     buildtool::PkgConfig pkgConfig;
8440     if (!pkgConfig.readFile("gtk+-2.0.pc"))
8441         return false;
8442     return true;
8447 static bool depTest()
8449     buildtool::DepTool deptool;
8450     deptool.setSourceDirectory("/dev/ink/inkscape/src");
8451     if (!deptool.generateDependencies("build.dep"))
8452         return false;
8453     std::vector<buildtool::DepRec> res =
8454            deptool.loadDepFile("build.dep");
8455     if (res.size() == 0)
8456         return false;
8457     return true;
8460 static bool popenTest()
8462     buildtool::Make make;
8463     buildtool::String out, err;
8464     bool ret = make.executeCommand("gcc xx.cpp", "", out, err);
8465     printf("Popen test:%d '%s' '%s'\n", ret, out.c_str(), err.c_str());
8466     return true;
8470 static bool propFileTest()
8472     buildtool::Make make;
8473     make.parsePropertyFile("test.prop", "test.");
8474     return true;
8476 */
8478 int main(int argc, char **argv)
8481     if (!parseOptions(argc, argv))
8482         return 1;
8483     /*
8484     if (!popenTest())
8485         return 1;
8487     if (!depTest())
8488         return 1;
8489     if (!propFileTest())
8490         return 1;
8491     if (runMake())
8492         return 1;
8493     */
8494     return 0;
8498 //########################################################################
8499 //# E N D 
8500 //########################################################################