Code

Improve dependencies using URI normalization. A little faster, too.
[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.9, 2007 Bob Jamison"
42 #include <stdio.h>
43 #include <fcntl.h>
44 #include <unistd.h>
45 #include <stdarg.h>
46 #include <sys/stat.h>
47 #include <time.h>
48 #include <sys/time.h>
49 #include <utime.h>
50 #include <dirent.h>
52 #include <string>
53 #include <map>
54 #include <set>
55 #include <vector>
57 #ifdef __WIN32__
58 #include <windows.h>
59 #endif
62 #include <errno.h>
65 //########################################################################
66 //# Definition of gettimeofday() for those who don't have it
67 //########################################################################
68 #ifndef HAVE_GETTIMEOFDAY
69 #include <sys/timeb.h>
71 struct timezone {
72       int tz_minuteswest; /* minutes west of Greenwich */
73       int tz_dsttime;     /* type of dst correction */
74     };
76 static int gettimeofday (struct timeval *tv, struct timezone *tz)
77 {
78    struct _timeb tb;
80    if (!tv)
81       return (-1);
83     _ftime (&tb);
84     tv->tv_sec  = tb.time;
85     tv->tv_usec = tb.millitm * 1000 + 500;
86     if (tz)
87         {
88         tz->tz_minuteswest = -60 * _timezone;
89         tz->tz_dsttime = _daylight;
90         }
91     return 0;
92 }
94 #endif
102 namespace buildtool
108 //########################################################################
109 //########################################################################
110 //##  R E G E X P
111 //########################################################################
112 //########################################################################
114 /**
115  * This is the T-Rex regular expression library, which we
116  * gratefully acknowledge.  It's clean code and small size allow
117  * us to embed it in BuildTool without adding a dependency
118  *
119  */    
121 //begin trex.h
123 #ifndef _TREX_H_
124 #define _TREX_H_
125 /***************************************************************
126     T-Rex a tiny regular expression library
128     Copyright (C) 2003-2006 Alberto Demichelis
130     This software is provided 'as-is', without any express 
131     or implied warranty. In no event will the authors be held 
132     liable for any damages arising from the use of this software.
134     Permission is granted to anyone to use this software for 
135     any purpose, including commercial applications, and to alter
136     it and redistribute it freely, subject to the following restrictions:
138         1. The origin of this software must not be misrepresented;
139         you must not claim that you wrote the original software.
140         If you use this software in a product, an acknowledgment
141         in the product documentation would be appreciated but
142         is not required.
144         2. Altered source versions must be plainly marked as such,
145         and must not be misrepresented as being the original software.
147         3. This notice may not be removed or altered from any
148         source distribution.
150 ****************************************************************/
152 #ifdef _UNICODE
153 #define TRexChar unsigned short
154 #define MAX_CHAR 0xFFFF
155 #define _TREXC(c) L##c 
156 #define trex_strlen wcslen
157 #define trex_printf wprintf
158 #else
159 #define TRexChar char
160 #define MAX_CHAR 0xFF
161 #define _TREXC(c) (c) 
162 #define trex_strlen strlen
163 #define trex_printf printf
164 #endif
166 #ifndef TREX_API
167 #define TREX_API extern
168 #endif
170 #define TRex_True 1
171 #define TRex_False 0
173 typedef unsigned int TRexBool;
174 typedef struct TRex TRex;
176 typedef struct {
177     const TRexChar *begin;
178     int len;
179 } TRexMatch;
181 TREX_API TRex *trex_compile(const TRexChar *pattern,const TRexChar **error);
182 TREX_API void trex_free(TRex *exp);
183 TREX_API TRexBool trex_match(TRex* exp,const TRexChar* text);
184 TREX_API TRexBool trex_search(TRex* exp,const TRexChar* text, const TRexChar** out_begin, const TRexChar** out_end);
185 TREX_API TRexBool trex_searchrange(TRex* exp,const TRexChar* text_begin,const TRexChar* text_end,const TRexChar** out_begin, const TRexChar** out_end);
186 TREX_API int trex_getsubexpcount(TRex* exp);
187 TREX_API TRexBool trex_getsubexp(TRex* exp, int n, TRexMatch *subexp);
189 #endif
191 //end trex.h
193 //start trex.c
196 #include <stdio.h>
197 #include <string>
199 /* see copyright notice in trex.h */
200 #include <string.h>
201 #include <stdlib.h>
202 #include <ctype.h>
203 #include <setjmp.h>
204 //#include "trex.h"
206 #ifdef _UINCODE
207 #define scisprint iswprint
208 #define scstrlen wcslen
209 #define scprintf wprintf
210 #define _SC(x) L(x)
211 #else
212 #define scisprint isprint
213 #define scstrlen strlen
214 #define scprintf printf
215 #define _SC(x) (x)
216 #endif
218 #ifdef _DEBUG
219 #include <stdio.h>
221 static const TRexChar *g_nnames[] =
223     _SC("NONE"),_SC("OP_GREEDY"),    _SC("OP_OR"),
224     _SC("OP_EXPR"),_SC("OP_NOCAPEXPR"),_SC("OP_DOT"),    _SC("OP_CLASS"),
225     _SC("OP_CCLASS"),_SC("OP_NCLASS"),_SC("OP_RANGE"),_SC("OP_CHAR"),
226     _SC("OP_EOL"),_SC("OP_BOL"),_SC("OP_WB")
227 };
229 #endif
230 #define OP_GREEDY        (MAX_CHAR+1) // * + ? {n}
231 #define OP_OR            (MAX_CHAR+2)
232 #define OP_EXPR            (MAX_CHAR+3) //parentesis ()
233 #define OP_NOCAPEXPR    (MAX_CHAR+4) //parentesis (?:)
234 #define OP_DOT            (MAX_CHAR+5)
235 #define OP_CLASS        (MAX_CHAR+6)
236 #define OP_CCLASS        (MAX_CHAR+7)
237 #define OP_NCLASS        (MAX_CHAR+8) //negates class the [^
238 #define OP_RANGE        (MAX_CHAR+9)
239 #define OP_CHAR            (MAX_CHAR+10)
240 #define OP_EOL            (MAX_CHAR+11)
241 #define OP_BOL            (MAX_CHAR+12)
242 #define OP_WB            (MAX_CHAR+13)
244 #define TREX_SYMBOL_ANY_CHAR ('.')
245 #define TREX_SYMBOL_GREEDY_ONE_OR_MORE ('+')
246 #define TREX_SYMBOL_GREEDY_ZERO_OR_MORE ('*')
247 #define TREX_SYMBOL_GREEDY_ZERO_OR_ONE ('?')
248 #define TREX_SYMBOL_BRANCH ('|')
249 #define TREX_SYMBOL_END_OF_STRING ('$')
250 #define TREX_SYMBOL_BEGINNING_OF_STRING ('^')
251 #define TREX_SYMBOL_ESCAPE_CHAR ('\\')
254 typedef int TRexNodeType;
256 typedef struct tagTRexNode{
257     TRexNodeType type;
258     int left;
259     int right;
260     int next;
261 }TRexNode;
263 struct TRex{
264     const TRexChar *_eol;
265     const TRexChar *_bol;
266     const TRexChar *_p;
267     int _first;
268     int _op;
269     TRexNode *_nodes;
270     int _nallocated;
271     int _nsize;
272     int _nsubexpr;
273     TRexMatch *_matches;
274     int _currsubexp;
275     void *_jmpbuf;
276     const TRexChar **_error;
277 };
279 static int trex_list(TRex *exp);
281 static int trex_newnode(TRex *exp, TRexNodeType type)
283     TRexNode n;
284     int newid;
285     n.type = type;
286     n.next = n.right = n.left = -1;
287     if(type == OP_EXPR)
288         n.right = exp->_nsubexpr++;
289     if(exp->_nallocated < (exp->_nsize + 1)) {
290         //int oldsize = exp->_nallocated;
291         exp->_nallocated *= 2;
292         exp->_nodes = (TRexNode *)realloc(exp->_nodes, exp->_nallocated * sizeof(TRexNode));
293     }
294     exp->_nodes[exp->_nsize++] = n;
295     newid = exp->_nsize - 1;
296     return (int)newid;
299 static void trex_error(TRex *exp,const TRexChar *error)
301     if(exp->_error) *exp->_error = error;
302     longjmp(*((jmp_buf*)exp->_jmpbuf),-1);
305 static void trex_expect(TRex *exp, int n){
306     if((*exp->_p) != n) 
307         trex_error(exp, _SC("expected paren"));
308     exp->_p++;
311 static TRexChar trex_escapechar(TRex *exp)
313     if(*exp->_p == TREX_SYMBOL_ESCAPE_CHAR){
314         exp->_p++;
315         switch(*exp->_p) {
316         case 'v': exp->_p++; return '\v';
317         case 'n': exp->_p++; return '\n';
318         case 't': exp->_p++; return '\t';
319         case 'r': exp->_p++; return '\r';
320         case 'f': exp->_p++; return '\f';
321         default: return (*exp->_p++);
322         }
323     } else if(!scisprint(*exp->_p)) trex_error(exp,_SC("letter expected"));
324     return (*exp->_p++);
327 static int trex_charclass(TRex *exp,int classid)
329     int n = trex_newnode(exp,OP_CCLASS);
330     exp->_nodes[n].left = classid;
331     return n;
334 static int trex_charnode(TRex *exp,TRexBool isclass)
336     TRexChar t;
337     if(*exp->_p == TREX_SYMBOL_ESCAPE_CHAR) {
338         exp->_p++;
339         switch(*exp->_p) {
340             case 'n': exp->_p++; return trex_newnode(exp,'\n');
341             case 't': exp->_p++; return trex_newnode(exp,'\t');
342             case 'r': exp->_p++; return trex_newnode(exp,'\r');
343             case 'f': exp->_p++; return trex_newnode(exp,'\f');
344             case 'v': exp->_p++; return trex_newnode(exp,'\v');
345             case 'a': case 'A': case 'w': case 'W': case 's': case 'S': 
346             case 'd': case 'D': case 'x': case 'X': case 'c': case 'C': 
347             case 'p': case 'P': case 'l': case 'u': 
348                 {
349                 t = *exp->_p; exp->_p++; 
350                 return trex_charclass(exp,t);
351                 }
352             case 'b': 
353             case 'B':
354                 if(!isclass) {
355                     int node = trex_newnode(exp,OP_WB);
356                     exp->_nodes[node].left = *exp->_p;
357                     exp->_p++; 
358                     return node;
359                 } //else default
360             default: 
361                 t = *exp->_p; exp->_p++; 
362                 return trex_newnode(exp,t);
363         }
364     }
365     else if(!scisprint(*exp->_p)) {
366         
367         trex_error(exp,_SC("letter expected"));
368     }
369     t = *exp->_p; exp->_p++; 
370     return trex_newnode(exp,t);
372 static int trex_class(TRex *exp)
374     int ret = -1;
375     int first = -1,chain;
376     if(*exp->_p == TREX_SYMBOL_BEGINNING_OF_STRING){
377         ret = trex_newnode(exp,OP_NCLASS);
378         exp->_p++;
379     }else ret = trex_newnode(exp,OP_CLASS);
380     
381     if(*exp->_p == ']') trex_error(exp,_SC("empty class"));
382     chain = ret;
383     while(*exp->_p != ']' && exp->_p != exp->_eol) {
384         if(*exp->_p == '-' && first != -1){ 
385             int r,t;
386             if(*exp->_p++ == ']') trex_error(exp,_SC("unfinished range"));
387             r = trex_newnode(exp,OP_RANGE);
388             if(first>*exp->_p) trex_error(exp,_SC("invalid range"));
389             if(exp->_nodes[first].type == OP_CCLASS) trex_error(exp,_SC("cannot use character classes in ranges"));
390             exp->_nodes[r].left = exp->_nodes[first].type;
391             t = trex_escapechar(exp);
392             exp->_nodes[r].right = t;
393             exp->_nodes[chain].next = r;
394             chain = r;
395             first = -1;
396         }
397         else{
398             if(first!=-1){
399                 int c = first;
400                 exp->_nodes[chain].next = c;
401                 chain = c;
402                 first = trex_charnode(exp,TRex_True);
403             }
404             else{
405                 first = trex_charnode(exp,TRex_True);
406             }
407         }
408     }
409     if(first!=-1){
410         int c = first;
411         exp->_nodes[chain].next = c;
412         chain = c;
413         first = -1;
414     }
415     /* hack? */
416     exp->_nodes[ret].left = exp->_nodes[ret].next;
417     exp->_nodes[ret].next = -1;
418     return ret;
421 static int trex_parsenumber(TRex *exp)
423     int ret = *exp->_p-'0';
424     int positions = 10;
425     exp->_p++;
426     while(isdigit(*exp->_p)) {
427         ret = ret*10+(*exp->_p++-'0');
428         if(positions==1000000000) trex_error(exp,_SC("overflow in numeric constant"));
429         positions *= 10;
430     };
431     return ret;
434 static int trex_element(TRex *exp)
436     int ret = -1;
437     switch(*exp->_p)
438     {
439     case '(': {
440         int expr,newn;
441         exp->_p++;
444         if(*exp->_p =='?') {
445             exp->_p++;
446             trex_expect(exp,':');
447             expr = trex_newnode(exp,OP_NOCAPEXPR);
448         }
449         else
450             expr = trex_newnode(exp,OP_EXPR);
451         newn = trex_list(exp);
452         exp->_nodes[expr].left = newn;
453         ret = expr;
454         trex_expect(exp,')');
455               }
456               break;
457     case '[':
458         exp->_p++;
459         ret = trex_class(exp);
460         trex_expect(exp,']');
461         break;
462     case TREX_SYMBOL_END_OF_STRING: exp->_p++; ret = trex_newnode(exp,OP_EOL);break;
463     case TREX_SYMBOL_ANY_CHAR: exp->_p++; ret = trex_newnode(exp,OP_DOT);break;
464     default:
465         ret = trex_charnode(exp,TRex_False);
466         break;
467     }
469     {
470         int op;
471         TRexBool isgreedy = TRex_False;
472         unsigned short p0 = 0, p1 = 0;
473         switch(*exp->_p){
474             case TREX_SYMBOL_GREEDY_ZERO_OR_MORE: p0 = 0; p1 = 0xFFFF; exp->_p++; isgreedy = TRex_True; break;
475             case TREX_SYMBOL_GREEDY_ONE_OR_MORE: p0 = 1; p1 = 0xFFFF; exp->_p++; isgreedy = TRex_True; break;
476             case TREX_SYMBOL_GREEDY_ZERO_OR_ONE: p0 = 0; p1 = 1; exp->_p++; isgreedy = TRex_True; break;
477             case '{':
478                 exp->_p++;
479                 if(!isdigit(*exp->_p)) trex_error(exp,_SC("number expected"));
480                 p0 = (unsigned short)trex_parsenumber(exp);
481                 /*******************************/
482                 switch(*exp->_p) {
483             case '}':
484                 p1 = p0; exp->_p++;
485                 break;
486             case ',':
487                 exp->_p++;
488                 p1 = 0xFFFF;
489                 if(isdigit(*exp->_p)){
490                     p1 = (unsigned short)trex_parsenumber(exp);
491                 }
492                 trex_expect(exp,'}');
493                 break;
494             default:
495                 trex_error(exp,_SC(", or } expected"));
496         }
497         /*******************************/
498         isgreedy = TRex_True; 
499         break;
501         }
502         if(isgreedy) {
503             int nnode = trex_newnode(exp,OP_GREEDY);
504             op = OP_GREEDY;
505             exp->_nodes[nnode].left = ret;
506             exp->_nodes[nnode].right = ((p0)<<16)|p1;
507             ret = nnode;
508         }
509     }
510     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')) {
511         int nnode = trex_element(exp);
512         exp->_nodes[ret].next = nnode;
513     }
515     return ret;
518 static int trex_list(TRex *exp)
520     int ret=-1,e;
521     if(*exp->_p == TREX_SYMBOL_BEGINNING_OF_STRING) {
522         exp->_p++;
523         ret = trex_newnode(exp,OP_BOL);
524     }
525     e = trex_element(exp);
526     if(ret != -1) {
527         exp->_nodes[ret].next = e;
528     }
529     else ret = e;
531     if(*exp->_p == TREX_SYMBOL_BRANCH) {
532         int temp,tright;
533         exp->_p++;
534         temp = trex_newnode(exp,OP_OR);
535         exp->_nodes[temp].left = ret;
536         tright = trex_list(exp);
537         exp->_nodes[temp].right = tright;
538         ret = temp;
539     }
540     return ret;
543 static TRexBool trex_matchcclass(int cclass,TRexChar c)
545     switch(cclass) {
546     case 'a': return isalpha(c)?TRex_True:TRex_False;
547     case 'A': return !isalpha(c)?TRex_True:TRex_False;
548     case 'w': return (isalnum(c) || c == '_')?TRex_True:TRex_False;
549     case 'W': return (!isalnum(c) && c != '_')?TRex_True:TRex_False;
550     case 's': return isspace(c)?TRex_True:TRex_False;
551     case 'S': return !isspace(c)?TRex_True:TRex_False;
552     case 'd': return isdigit(c)?TRex_True:TRex_False;
553     case 'D': return !isdigit(c)?TRex_True:TRex_False;
554     case 'x': return isxdigit(c)?TRex_True:TRex_False;
555     case 'X': return !isxdigit(c)?TRex_True:TRex_False;
556     case 'c': return iscntrl(c)?TRex_True:TRex_False;
557     case 'C': return !iscntrl(c)?TRex_True:TRex_False;
558     case 'p': return ispunct(c)?TRex_True:TRex_False;
559     case 'P': return !ispunct(c)?TRex_True:TRex_False;
560     case 'l': return islower(c)?TRex_True:TRex_False;
561     case 'u': return isupper(c)?TRex_True:TRex_False;
562     }
563     return TRex_False; /*cannot happen*/
566 static TRexBool trex_matchclass(TRex* exp,TRexNode *node,TRexChar c)
568     do {
569         switch(node->type) {
570             case OP_RANGE:
571                 if(c >= node->left && c <= node->right) return TRex_True;
572                 break;
573             case OP_CCLASS:
574                 if(trex_matchcclass(node->left,c)) return TRex_True;
575                 break;
576             default:
577                 if(c == node->type)return TRex_True;
578         }
579     } while((node->next != -1) && (node = &exp->_nodes[node->next]));
580     return TRex_False;
583 static const TRexChar *trex_matchnode(TRex* exp,TRexNode *node,const TRexChar *str,TRexNode *next)
585     
586     TRexNodeType type = node->type;
587     switch(type) {
588     case OP_GREEDY: {
589         //TRexNode *greedystop = (node->next != -1) ? &exp->_nodes[node->next] : NULL;
590         TRexNode *greedystop = NULL;
591         int p0 = (node->right >> 16)&0x0000FFFF, p1 = node->right&0x0000FFFF, nmaches = 0;
592         const TRexChar *s=str, *good = str;
594         if(node->next != -1) {
595             greedystop = &exp->_nodes[node->next];
596         }
597         else {
598             greedystop = next;
599         }
601         while((nmaches == 0xFFFF || nmaches < p1)) {
603             const TRexChar *stop;
604             if(!(s = trex_matchnode(exp,&exp->_nodes[node->left],s,greedystop)))
605                 break;
606             nmaches++;
607             good=s;
608             if(greedystop) {
609                 //checks that 0 matches satisfy the expression(if so skips)
610                 //if not would always stop(for instance if is a '?')
611                 if(greedystop->type != OP_GREEDY ||
612                 (greedystop->type == OP_GREEDY && ((greedystop->right >> 16)&0x0000FFFF) != 0))
613                 {
614                     TRexNode *gnext = NULL;
615                     if(greedystop->next != -1) {
616                         gnext = &exp->_nodes[greedystop->next];
617                     }else if(next && next->next != -1){
618                         gnext = &exp->_nodes[next->next];
619                     }
620                     stop = trex_matchnode(exp,greedystop,s,gnext);
621                     if(stop) {
622                         //if satisfied stop it
623                         if(p0 == p1 && p0 == nmaches) break;
624                         else if(nmaches >= p0 && p1 == 0xFFFF) break;
625                         else if(nmaches >= p0 && nmaches <= p1) break;
626                     }
627                 }
628             }
629             
630             if(s >= exp->_eol)
631                 break;
632         }
633         if(p0 == p1 && p0 == nmaches) return good;
634         else if(nmaches >= p0 && p1 == 0xFFFF) return good;
635         else if(nmaches >= p0 && nmaches <= p1) return good;
636         return NULL;
637     }
638     case OP_OR: {
639             const TRexChar *asd = str;
640             TRexNode *temp=&exp->_nodes[node->left];
641             while( (asd = trex_matchnode(exp,temp,asd,NULL)) ) {
642                 if(temp->next != -1)
643                     temp = &exp->_nodes[temp->next];
644                 else
645                     return asd;
646             }
647             asd = str;
648             temp = &exp->_nodes[node->right];
649             while( (asd = trex_matchnode(exp,temp,asd,NULL)) ) {
650                 if(temp->next != -1)
651                     temp = &exp->_nodes[temp->next];
652                 else
653                     return asd;
654             }
655             return NULL;
656             break;
657     }
658     case OP_EXPR:
659     case OP_NOCAPEXPR:{
660             TRexNode *n = &exp->_nodes[node->left];
661             const TRexChar *cur = str;
662             int capture = -1;
663             if(node->type != OP_NOCAPEXPR && node->right == exp->_currsubexp) {
664                 capture = exp->_currsubexp;
665                 exp->_matches[capture].begin = cur;
666                 exp->_currsubexp++;
667             }
668             
669             do {
670                 TRexNode *subnext = NULL;
671                 if(n->next != -1) {
672                     subnext = &exp->_nodes[n->next];
673                 }else {
674                     subnext = next;
675                 }
676                 if(!(cur = trex_matchnode(exp,n,cur,subnext))) {
677                     if(capture != -1){
678                         exp->_matches[capture].begin = 0;
679                         exp->_matches[capture].len = 0;
680                     }
681                     return NULL;
682                 }
683             } while((n->next != -1) && (n = &exp->_nodes[n->next]));
685             if(capture != -1) 
686                 exp->_matches[capture].len = cur - exp->_matches[capture].begin;
687             return cur;
688     }                 
689     case OP_WB:
690         if(str == exp->_bol && !isspace(*str)
691          || (str == exp->_eol && !isspace(*(str-1)))
692          || (!isspace(*str) && isspace(*(str+1)))
693          || (isspace(*str) && !isspace(*(str+1))) ) {
694             return (node->left == 'b')?str:NULL;
695         }
696         return (node->left == 'b')?NULL:str;
697     case OP_BOL:
698         if(str == exp->_bol) return str;
699         return NULL;
700     case OP_EOL:
701         if(str == exp->_eol) return str;
702         return NULL;
703     case OP_DOT:{
704         *str++;
705                 }
706         return str;
707     case OP_NCLASS:
708     case OP_CLASS:
709         if(trex_matchclass(exp,&exp->_nodes[node->left],*str)?(type == OP_CLASS?TRex_True:TRex_False):(type == OP_NCLASS?TRex_True:TRex_False)) {
710             *str++;
711             return str;
712         }
713         return NULL;
714     case OP_CCLASS:
715         if(trex_matchcclass(node->left,*str)) {
716             *str++;
717             return str;
718         }
719         return NULL;
720     default: /* char */
721         if(*str != node->type) return NULL;
722         *str++;
723         return str;
724     }
725     return NULL;
728 /* public api */
729 TRex *trex_compile(const TRexChar *pattern,const TRexChar **error)
731     TRex *exp = (TRex *)malloc(sizeof(TRex));
732     exp->_eol = exp->_bol = NULL;
733     exp->_p = pattern;
734     exp->_nallocated = (int)scstrlen(pattern) * sizeof(TRexChar);
735     exp->_nodes = (TRexNode *)malloc(exp->_nallocated * sizeof(TRexNode));
736     exp->_nsize = 0;
737     exp->_matches = 0;
738     exp->_nsubexpr = 0;
739     exp->_first = trex_newnode(exp,OP_EXPR);
740     exp->_error = error;
741     exp->_jmpbuf = malloc(sizeof(jmp_buf));
742     if(setjmp(*((jmp_buf*)exp->_jmpbuf)) == 0) {
743         int res = trex_list(exp);
744         exp->_nodes[exp->_first].left = res;
745         if(*exp->_p!='\0')
746             trex_error(exp,_SC("unexpected character"));
747 #ifdef _DEBUG
748         {
749             int nsize,i;
750             TRexNode *t;
751             nsize = exp->_nsize;
752             t = &exp->_nodes[0];
753             scprintf(_SC("\n"));
754             for(i = 0;i < nsize; i++) {
755                 if(exp->_nodes[i].type>MAX_CHAR)
756                     scprintf(_SC("[%02d] %10s "),i,g_nnames[exp->_nodes[i].type-MAX_CHAR]);
757                 else
758                     scprintf(_SC("[%02d] %10c "),i,exp->_nodes[i].type);
759                 scprintf(_SC("left %02d right %02d next %02d\n"),exp->_nodes[i].left,exp->_nodes[i].right,exp->_nodes[i].next);
760             }
761             scprintf(_SC("\n"));
762         }
763 #endif
764         exp->_matches = (TRexMatch *) malloc(exp->_nsubexpr * sizeof(TRexMatch));
765         memset(exp->_matches,0,exp->_nsubexpr * sizeof(TRexMatch));
766     }
767     else{
768         trex_free(exp);
769         return NULL;
770     }
771     return exp;
774 void trex_free(TRex *exp)
776     if(exp)    {
777         if(exp->_nodes) free(exp->_nodes);
778         if(exp->_jmpbuf) free(exp->_jmpbuf);
779         if(exp->_matches) free(exp->_matches);
780         free(exp);
781     }
784 TRexBool trex_match(TRex* exp,const TRexChar* text)
786     const TRexChar* res = NULL;
787     exp->_bol = text;
788     exp->_eol = text + scstrlen(text);
789     exp->_currsubexp = 0;
790     res = trex_matchnode(exp,exp->_nodes,text,NULL);
791     if(res == NULL || res != exp->_eol)
792         return TRex_False;
793     return TRex_True;
796 TRexBool trex_searchrange(TRex* exp,const TRexChar* text_begin,const TRexChar* text_end,const TRexChar** out_begin, const TRexChar** out_end)
798     const TRexChar *cur = NULL;
799     int node = exp->_first;
800     if(text_begin >= text_end) return TRex_False;
801     exp->_bol = text_begin;
802     exp->_eol = text_end;
803     do {
804         cur = text_begin;
805         while(node != -1) {
806             exp->_currsubexp = 0;
807             cur = trex_matchnode(exp,&exp->_nodes[node],cur,NULL);
808             if(!cur)
809                 break;
810             node = exp->_nodes[node].next;
811         }
812         *text_begin++;
813     } while(cur == NULL && text_begin != text_end);
815     if(cur == NULL)
816         return TRex_False;
818     --text_begin;
820     if(out_begin) *out_begin = text_begin;
821     if(out_end) *out_end = cur;
822     return TRex_True;
825 TRexBool trex_search(TRex* exp,const TRexChar* text, const TRexChar** out_begin, const TRexChar** out_end)
827     return trex_searchrange(exp,text,text + scstrlen(text),out_begin,out_end);
830 int trex_getsubexpcount(TRex* exp)
832     return exp->_nsubexpr;
835 TRexBool trex_getsubexp(TRex* exp, int n, TRexMatch *subexp)
837     if( n<0 || n >= exp->_nsubexpr) return TRex_False;
838     *subexp = exp->_matches[n];
839     return TRex_True;
843 //########################################################################
844 //########################################################################
845 //##  E N D    R E G E X P
846 //########################################################################
847 //########################################################################
853 //########################################################################
854 //########################################################################
855 //##  X M L
856 //########################################################################
857 //########################################################################
859 // Note:  This mini-dom library comes from Pedro, another little project
860 // of mine.
862 typedef std::string String;
863 typedef unsigned int XMLCh;
866 class Namespace
868 public:
869     Namespace()
870         {}
872     Namespace(const String &prefixArg, const String &namespaceURIArg)
873         {
874         prefix       = prefixArg;
875         namespaceURI = namespaceURIArg;
876         }
878     Namespace(const Namespace &other)
879         {
880         assign(other);
881         }
883     Namespace &operator=(const Namespace &other)
884         {
885         assign(other);
886         return *this;
887         }
889     virtual ~Namespace()
890         {}
892     virtual String getPrefix()
893         { return prefix; }
895     virtual String getNamespaceURI()
896         { return namespaceURI; }
898 protected:
900     void assign(const Namespace &other)
901         {
902         prefix       = other.prefix;
903         namespaceURI = other.namespaceURI;
904         }
906     String prefix;
907     String namespaceURI;
909 };
911 class Attribute
913 public:
914     Attribute()
915         {}
917     Attribute(const String &nameArg, const String &valueArg)
918         {
919         name  = nameArg;
920         value = valueArg;
921         }
923     Attribute(const Attribute &other)
924         {
925         assign(other);
926         }
928     Attribute &operator=(const Attribute &other)
929         {
930         assign(other);
931         return *this;
932         }
934     virtual ~Attribute()
935         {}
937     virtual String getName()
938         { return name; }
940     virtual String getValue()
941         { return value; }
943 protected:
945     void assign(const Attribute &other)
946         {
947         name  = other.name;
948         value = other.value;
949         }
951     String name;
952     String value;
954 };
957 class Element
959 friend class Parser;
961 public:
962     Element()
963         {
964         init();
965         }
967     Element(const String &nameArg)
968         {
969         init();
970         name   = nameArg;
971         }
973     Element(const String &nameArg, const String &valueArg)
974         {
975         init();
976         name   = nameArg;
977         value  = valueArg;
978         }
980     Element(const Element &other)
981         {
982         assign(other);
983         }
985     Element &operator=(const Element &other)
986         {
987         assign(other);
988         return *this;
989         }
991     virtual Element *clone();
993     virtual ~Element()
994         {
995         for (unsigned int i=0 ; i<children.size() ; i++)
996             delete children[i];
997         }
999     virtual String getName()
1000         { return name; }
1002     virtual String getValue()
1003         { return value; }
1005     Element *getParent()
1006         { return parent; }
1008     std::vector<Element *> getChildren()
1009         { return children; }
1011     std::vector<Element *> findElements(const String &name);
1013     String getAttribute(const String &name);
1015     std::vector<Attribute> &getAttributes()
1016         { return attributes; } 
1018     String getTagAttribute(const String &tagName, const String &attrName);
1020     String getTagValue(const String &tagName);
1022     void addChild(Element *child);
1024     void addAttribute(const String &name, const String &value);
1026     void addNamespace(const String &prefix, const String &namespaceURI);
1029     /**
1030      * Prettyprint an XML tree to an output stream.  Elements are indented
1031      * according to element hierarchy.
1032      * @param f a stream to receive the output
1033      * @param elem the element to output
1034      */
1035     void writeIndented(FILE *f);
1037     /**
1038      * Prettyprint an XML tree to standard output.  This is the equivalent of
1039      * writeIndented(stdout).
1040      * @param elem the element to output
1041      */
1042     void print();
1043     
1044     int getLine()
1045         { return line; }
1047 protected:
1049     void init()
1050         {
1051         parent = NULL;
1052         line   = 0;
1053         }
1055     void assign(const Element &other)
1056         {
1057         parent     = other.parent;
1058         children   = other.children;
1059         attributes = other.attributes;
1060         namespaces = other.namespaces;
1061         name       = other.name;
1062         value      = other.value;
1063         line       = other.line;
1064         }
1066     void findElementsRecursive(std::vector<Element *>&res, const String &name);
1068     void writeIndentedRecursive(FILE *f, int indent);
1070     Element *parent;
1072     std::vector<Element *>children;
1074     std::vector<Attribute> attributes;
1075     std::vector<Namespace> namespaces;
1077     String name;
1078     String value;
1079     
1080     int line;
1081 };
1087 class Parser
1089 public:
1090     /**
1091      * Constructor
1092      */
1093     Parser()
1094         { init(); }
1096     virtual ~Parser()
1097         {}
1099     /**
1100      * Parse XML in a char buffer.
1101      * @param buf a character buffer to parse
1102      * @param pos position to start parsing
1103      * @param len number of chars, from pos, to parse.
1104      * @return a pointer to the root of the XML document;
1105      */
1106     Element *parse(const char *buf,int pos,int len);
1108     /**
1109      * Parse XML in a char buffer.
1110      * @param buf a character buffer to parse
1111      * @param pos position to start parsing
1112      * @param len number of chars, from pos, to parse.
1113      * @return a pointer to the root of the XML document;
1114      */
1115     Element *parse(const String &buf);
1117     /**
1118      * Parse a named XML file.  The file is loaded like a data file;
1119      * the original format is not preserved.
1120      * @param fileName the name of the file to read
1121      * @return a pointer to the root of the XML document;
1122      */
1123     Element *parseFile(const String &fileName);
1125     /**
1126      * Utility method to preprocess a string for XML
1127      * output, escaping its entities.
1128      * @param str the string to encode
1129      */
1130     static String encode(const String &str);
1132     /**
1133      *  Removes whitespace from beginning and end of a string
1134      */
1135     String trim(const String &s);
1137 private:
1139     void init()
1140         {
1141         keepGoing       = true;
1142         currentNode     = NULL;
1143         parselen        = 0;
1144         parsebuf        = NULL;
1145         currentPosition = 0;
1146         }
1148     int countLines(int begin, int end);
1150     void getLineAndColumn(int pos, int *lineNr, int *colNr);
1152     void error(char *fmt, ...);
1154     int peek(int pos);
1156     int match(int pos, const char *text);
1158     int skipwhite(int p);
1160     int getWord(int p0, String &buf);
1162     int getQuoted(int p0, String &buf, int do_i_parse);
1164     int parseVersion(int p0);
1166     int parseDoctype(int p0);
1168     int parseElement(int p0, Element *par,int depth);
1170     Element *parse(XMLCh *buf,int pos,int len);
1172     bool       keepGoing;
1173     Element    *currentNode;
1174     int        parselen;
1175     XMLCh      *parsebuf;
1176     String     cdatabuf;
1177     int        currentPosition;
1178 };
1183 //########################################################################
1184 //# E L E M E N T
1185 //########################################################################
1187 Element *Element::clone()
1189     Element *elem = new Element(name, value);
1190     elem->parent     = parent;
1191     elem->attributes = attributes;
1192     elem->namespaces = namespaces;
1193     elem->line       = line;
1195     std::vector<Element *>::iterator iter;
1196     for (iter = children.begin(); iter != children.end() ; iter++)
1197         {
1198         elem->addChild((*iter)->clone());
1199         }
1200     return elem;
1204 void Element::findElementsRecursive(std::vector<Element *>&res, const String &name)
1206     if (getName() == name)
1207         {
1208         res.push_back(this);
1209         }
1210     for (unsigned int i=0; i<children.size() ; i++)
1211         children[i]->findElementsRecursive(res, name);
1214 std::vector<Element *> Element::findElements(const String &name)
1216     std::vector<Element *> res;
1217     findElementsRecursive(res, name);
1218     return res;
1221 String Element::getAttribute(const String &name)
1223     for (unsigned int i=0 ; i<attributes.size() ; i++)
1224         if (attributes[i].getName() ==name)
1225             return attributes[i].getValue();
1226     return "";
1229 String Element::getTagAttribute(const String &tagName, const String &attrName)
1231     std::vector<Element *>elems = findElements(tagName);
1232     if (elems.size() <1)
1233         return "";
1234     String res = elems[0]->getAttribute(attrName);
1235     return res;
1238 String Element::getTagValue(const String &tagName)
1240     std::vector<Element *>elems = findElements(tagName);
1241     if (elems.size() <1)
1242         return "";
1243     String res = elems[0]->getValue();
1244     return res;
1247 void Element::addChild(Element *child)
1249     if (!child)
1250         return;
1251     child->parent = this;
1252     children.push_back(child);
1256 void Element::addAttribute(const String &name, const String &value)
1258     Attribute attr(name, value);
1259     attributes.push_back(attr);
1262 void Element::addNamespace(const String &prefix, const String &namespaceURI)
1264     Namespace ns(prefix, namespaceURI);
1265     namespaces.push_back(ns);
1268 void Element::writeIndentedRecursive(FILE *f, int indent)
1270     int i;
1271     if (!f)
1272         return;
1273     //Opening tag, and attributes
1274     for (i=0;i<indent;i++)
1275         fputc(' ',f);
1276     fprintf(f,"<%s",name.c_str());
1277     for (unsigned int i=0 ; i<attributes.size() ; i++)
1278         {
1279         fprintf(f," %s=\"%s\"",
1280               attributes[i].getName().c_str(),
1281               attributes[i].getValue().c_str());
1282         }
1283     for (unsigned int i=0 ; i<namespaces.size() ; i++)
1284         {
1285         fprintf(f," xmlns:%s=\"%s\"",
1286               namespaces[i].getPrefix().c_str(),
1287               namespaces[i].getNamespaceURI().c_str());
1288         }
1289     fprintf(f,">\n");
1291     //Between the tags
1292     if (value.size() > 0)
1293         {
1294         for (int i=0;i<indent;i++)
1295             fputc(' ', f);
1296         fprintf(f," %s\n", value.c_str());
1297         }
1299     for (unsigned int i=0 ; i<children.size() ; i++)
1300         children[i]->writeIndentedRecursive(f, indent+2);
1302     //Closing tag
1303     for (int i=0; i<indent; i++)
1304         fputc(' ',f);
1305     fprintf(f,"</%s>\n", name.c_str());
1308 void Element::writeIndented(FILE *f)
1310     writeIndentedRecursive(f, 0);
1313 void Element::print()
1315     writeIndented(stdout);
1319 //########################################################################
1320 //# P A R S E R
1321 //########################################################################
1325 typedef struct
1326     {
1327     char *escaped;
1328     char value;
1329     } EntityEntry;
1331 static EntityEntry entities[] =
1333     { "&amp;" , '&'  },
1334     { "&lt;"  , '<'  },
1335     { "&gt;"  , '>'  },
1336     { "&apos;", '\'' },
1337     { "&quot;", '"'  },
1338     { NULL    , '\0' }
1339 };
1343 /**
1344  *  Removes whitespace from beginning and end of a string
1345  */
1346 String Parser::trim(const String &s)
1348     if (s.size() < 1)
1349         return s;
1350     
1351     //Find first non-ws char
1352     unsigned int begin = 0;
1353     for ( ; begin < s.size() ; begin++)
1354         {
1355         if (!isspace(s[begin]))
1356             break;
1357         }
1359     //Find first non-ws char, going in reverse
1360     unsigned int end = s.size() - 1;
1361     for ( ; end > begin ; end--)
1362         {
1363         if (!isspace(s[end]))
1364             break;
1365         }
1366     //trace("begin:%d  end:%d", begin, end);
1368     String res = s.substr(begin, end-begin+1);
1369     return res;
1373 int Parser::countLines(int begin, int end)
1375     int count = 0;
1376     for (int i=begin ; i<end ; i++)
1377         {
1378         XMLCh ch = parsebuf[i];
1379         if (ch == '\n' || ch == '\r')
1380             count++;
1381         }
1382     return count;
1386 void Parser::getLineAndColumn(int pos, int *lineNr, int *colNr)
1388     int line = 1;
1389     int col  = 1;
1390     for (long i=0 ; i<pos ; i++)
1391         {
1392         XMLCh ch = parsebuf[i];
1393         if (ch == '\n' || ch == '\r')
1394             {
1395             col = 0;
1396             line ++;
1397             }
1398         else
1399             col++;
1400         }
1401     *lineNr = line;
1402     *colNr  = col;
1407 void Parser::error(char *fmt, ...)
1409     int lineNr;
1410     int colNr;
1411     getLineAndColumn(currentPosition, &lineNr, &colNr);
1412     va_list args;
1413     fprintf(stderr, "xml error at line %d, column %d:", lineNr, colNr);
1414     va_start(args,fmt);
1415     vfprintf(stderr,fmt,args);
1416     va_end(args) ;
1417     fprintf(stderr, "\n");
1422 int Parser::peek(int pos)
1424     if (pos >= parselen)
1425         return -1;
1426     currentPosition = pos;
1427     int ch = parsebuf[pos];
1428     //printf("ch:%c\n", ch);
1429     return ch;
1434 String Parser::encode(const String &str)
1436     String ret;
1437     for (unsigned int i=0 ; i<str.size() ; i++)
1438         {
1439         XMLCh ch = (XMLCh)str[i];
1440         if (ch == '&')
1441             ret.append("&amp;");
1442         else if (ch == '<')
1443             ret.append("&lt;");
1444         else if (ch == '>')
1445             ret.append("&gt;");
1446         else if (ch == '\'')
1447             ret.append("&apos;");
1448         else if (ch == '"')
1449             ret.append("&quot;");
1450         else
1451             ret.push_back(ch);
1453         }
1454     return ret;
1458 int Parser::match(int p0, const char *text)
1460     int p = p0;
1461     while (*text)
1462         {
1463         if (peek(p) != *text)
1464             return p0;
1465         p++; text++;
1466         }
1467     return p;
1472 int Parser::skipwhite(int p)
1475     while (p<parselen)
1476         {
1477         int p2 = match(p, "<!--");
1478         if (p2 > p)
1479             {
1480             p = p2;
1481             while (p<parselen)
1482               {
1483               p2 = match(p, "-->");
1484               if (p2 > p)
1485                   {
1486                   p = p2;
1487                   break;
1488                   }
1489               p++;
1490               }
1491           }
1492       XMLCh b = peek(p);
1493       if (!isspace(b))
1494           break;
1495       p++;
1496       }
1497   return p;
1500 /* modify this to allow all chars for an element or attribute name*/
1501 int Parser::getWord(int p0, String &buf)
1503     int p = p0;
1504     while (p<parselen)
1505         {
1506         XMLCh b = peek(p);
1507         if (b<=' ' || b=='/' || b=='>' || b=='=')
1508             break;
1509         buf.push_back(b);
1510         p++;
1511         }
1512     return p;
1515 int Parser::getQuoted(int p0, String &buf, int do_i_parse)
1518     int p = p0;
1519     if (peek(p) != '"' && peek(p) != '\'')
1520         return p0;
1521     p++;
1523     while ( p<parselen )
1524         {
1525         XMLCh b = peek(p);
1526         if (b=='"' || b=='\'')
1527             break;
1528         if (b=='&' && do_i_parse)
1529             {
1530             bool found = false;
1531             for (EntityEntry *ee = entities ; ee->value ; ee++)
1532                 {
1533                 int p2 = match(p, ee->escaped);
1534                 if (p2>p)
1535                     {
1536                     buf.push_back(ee->value);
1537                     p = p2;
1538                     found = true;
1539                     break;
1540                     }
1541                 }
1542             if (!found)
1543                 {
1544                 error("unterminated entity");
1545                 return false;
1546                 }
1547             }
1548         else
1549             {
1550             buf.push_back(b);
1551             p++;
1552             }
1553         }
1554     return p;
1557 int Parser::parseVersion(int p0)
1559     //printf("### parseVersion: %d\n", p0);
1561     int p = p0;
1563     p = skipwhite(p0);
1565     if (peek(p) != '<')
1566         return p0;
1568     p++;
1569     if (p>=parselen || peek(p)!='?')
1570         return p0;
1572     p++;
1574     String buf;
1576     while (p<parselen)
1577         {
1578         XMLCh ch = peek(p);
1579         if (ch=='?')
1580             {
1581             p++;
1582             break;
1583             }
1584         buf.push_back(ch);
1585         p++;
1586         }
1588     if (peek(p) != '>')
1589         return p0;
1590     p++;
1592     //printf("Got version:%s\n",buf.c_str());
1593     return p;
1596 int Parser::parseDoctype(int p0)
1598     //printf("### parseDoctype: %d\n", p0);
1600     int p = p0;
1601     p = skipwhite(p);
1603     if (p>=parselen || peek(p)!='<')
1604         return p0;
1606     p++;
1608     if (peek(p)!='!' || peek(p+1)=='-')
1609         return p0;
1610     p++;
1612     String buf;
1613     while (p<parselen)
1614         {
1615         XMLCh ch = peek(p);
1616         if (ch=='>')
1617             {
1618             p++;
1619             break;
1620             }
1621         buf.push_back(ch);
1622         p++;
1623         }
1625     //printf("Got doctype:%s\n",buf.c_str());
1626     return p;
1631 int Parser::parseElement(int p0, Element *par,int lineNr)
1634     int p = p0;
1636     int p2 = p;
1638     p = skipwhite(p);
1640     //## Get open tag
1641     XMLCh ch = peek(p);
1642     if (ch!='<')
1643         return p0;
1645     int line, col;
1646     //getLineAndColumn(p, &line, &col);
1648     p++;
1650     String openTagName;
1651     p = skipwhite(p);
1652     p = getWord(p, openTagName);
1653     //printf("####tag :%s\n", openTagName.c_str());
1654     p = skipwhite(p);
1656     //Add element to tree
1657     Element *n = new Element(openTagName);
1658     n->line = lineNr + countLines(p0, p);
1659     n->parent = par;
1660     par->addChild(n);
1662     // Get attributes
1663     if (peek(p) != '>')
1664         {
1665         while (p<parselen)
1666             {
1667             p = skipwhite(p);
1668             ch = peek(p);
1669             //printf("ch:%c\n",ch);
1670             if (ch=='>')
1671                 break;
1672             else if (ch=='/' && p<parselen+1)
1673                 {
1674                 p++;
1675                 p = skipwhite(p);
1676                 ch = peek(p);
1677                 if (ch=='>')
1678                     {
1679                     p++;
1680                     //printf("quick close\n");
1681                     return p;
1682                     }
1683                 }
1684             String attrName;
1685             p2 = getWord(p, attrName);
1686             if (p2==p)
1687                 break;
1688             //printf("name:%s",buf);
1689             p=p2;
1690             p = skipwhite(p);
1691             ch = peek(p);
1692             //printf("ch:%c\n",ch);
1693             if (ch!='=')
1694                 break;
1695             p++;
1696             p = skipwhite(p);
1697             // ch = parsebuf[p];
1698             // printf("ch:%c\n",ch);
1699             String attrVal;
1700             p2 = getQuoted(p, attrVal, true);
1701             p=p2+1;
1702             //printf("name:'%s'   value:'%s'\n",attrName.c_str(),attrVal.c_str());
1703             char *namestr = (char *)attrName.c_str();
1704             if (strncmp(namestr, "xmlns:", 6)==0)
1705                 n->addNamespace(attrName, attrVal);
1706             else
1707                 n->addAttribute(attrName, attrVal);
1708             }
1709         }
1711     bool cdata = false;
1713     p++;
1714     // ### Get intervening data ### */
1715     String data;
1716     while (p<parselen)
1717         {
1718         //# COMMENT
1719         p2 = match(p, "<!--");
1720         if (!cdata && p2>p)
1721             {
1722             p = p2;
1723             while (p<parselen)
1724                 {
1725                 p2 = match(p, "-->");
1726                 if (p2 > p)
1727                     {
1728                     p = p2;
1729                     break;
1730                     }
1731                 p++;
1732                 }
1733             }
1735         ch = peek(p);
1736         //# END TAG
1737         if (ch=='<' && !cdata && peek(p+1)=='/')
1738             {
1739             break;
1740             }
1741         //# CDATA
1742         p2 = match(p, "<![CDATA[");
1743         if (p2 > p)
1744             {
1745             cdata = true;
1746             p = p2;
1747             continue;
1748             }
1750         //# CHILD ELEMENT
1751         if (ch == '<')
1752             {
1753             p2 = parseElement(p, n, lineNr + countLines(p0, p));
1754             if (p2 == p)
1755                 {
1756                 /*
1757                 printf("problem on element:%s.  p2:%d p:%d\n",
1758                       openTagName.c_str(), p2, p);
1759                 */
1760                 return p0;
1761                 }
1762             p = p2;
1763             continue;
1764             }
1765         //# ENTITY
1766         if (ch=='&' && !cdata)
1767             {
1768             bool found = false;
1769             for (EntityEntry *ee = entities ; ee->value ; ee++)
1770                 {
1771                 int p2 = match(p, ee->escaped);
1772                 if (p2>p)
1773                     {
1774                     data.push_back(ee->value);
1775                     p = p2;
1776                     found = true;
1777                     break;
1778                     }
1779                 }
1780             if (!found)
1781                 {
1782                 error("unterminated entity");
1783                 return -1;
1784                 }
1785             continue;
1786             }
1788         //# NONE OF THE ABOVE
1789         data.push_back(ch);
1790         p++;
1791         }/*while*/
1794     n->value = data;
1795     //printf("%d : data:%s\n",p,data.c_str());
1797     //## Get close tag
1798     p = skipwhite(p);
1799     ch = peek(p);
1800     if (ch != '<')
1801         {
1802         error("no < for end tag\n");
1803         return p0;
1804         }
1805     p++;
1806     ch = peek(p);
1807     if (ch != '/')
1808         {
1809         error("no / on end tag");
1810         return p0;
1811         }
1812     p++;
1813     ch = peek(p);
1814     p = skipwhite(p);
1815     String closeTagName;
1816     p = getWord(p, closeTagName);
1817     if (openTagName != closeTagName)
1818         {
1819         error("Mismatched closing tag.  Expected </%S>. Got '%S'.",
1820                 openTagName.c_str(), closeTagName.c_str());
1821         return p0;
1822         }
1823     p = skipwhite(p);
1824     if (peek(p) != '>')
1825         {
1826         error("no > on end tag for '%s'", closeTagName.c_str());
1827         return p0;
1828         }
1829     p++;
1830     // printf("close element:%s\n",closeTagName.c_str());
1831     p = skipwhite(p);
1832     return p;
1838 Element *Parser::parse(XMLCh *buf,int pos,int len)
1840     parselen = len;
1841     parsebuf = buf;
1842     Element *rootNode = new Element("root");
1843     pos = parseVersion(pos);
1844     pos = parseDoctype(pos);
1845     pos = parseElement(pos, rootNode, 1);
1846     return rootNode;
1850 Element *Parser::parse(const char *buf, int pos, int len)
1852     XMLCh *charbuf = new XMLCh[len + 1];
1853     long i = 0;
1854     for ( ; i < len ; i++)
1855         charbuf[i] = (XMLCh)buf[i];
1856     charbuf[i] = '\0';
1858     Element *n = parse(charbuf, pos, len);
1859     delete[] charbuf;
1860     return n;
1863 Element *Parser::parse(const String &buf)
1865     long len = (long)buf.size();
1866     XMLCh *charbuf = new XMLCh[len + 1];
1867     long i = 0;
1868     for ( ; i < len ; i++)
1869         charbuf[i] = (XMLCh)buf[i];
1870     charbuf[i] = '\0';
1872     Element *n = parse(charbuf, 0, len);
1873     delete[] charbuf;
1874     return n;
1877 Element *Parser::parseFile(const String &fileName)
1880     //##### LOAD INTO A CHAR BUF, THEN CONVERT TO XMLCh
1881     FILE *f = fopen(fileName.c_str(), "rb");
1882     if (!f)
1883         return NULL;
1885     struct stat  statBuf;
1886     if (fstat(fileno(f),&statBuf)<0)
1887         {
1888         fclose(f);
1889         return NULL;
1890         }
1891     long filelen = statBuf.st_size;
1893     //printf("length:%d\n",filelen);
1894     XMLCh *charbuf = new XMLCh[filelen + 1];
1895     for (XMLCh *p=charbuf ; !feof(f) ; p++)
1896         {
1897         *p = (XMLCh)fgetc(f);
1898         }
1899     fclose(f);
1900     charbuf[filelen] = '\0';
1903     /*
1904     printf("nrbytes:%d\n",wc_count);
1905     printf("buf:%ls\n======\n",charbuf);
1906     */
1907     Element *n = parse(charbuf, 0, filelen);
1908     delete[] charbuf;
1909     return n;
1912 //########################################################################
1913 //########################################################################
1914 //##  E N D    X M L
1915 //########################################################################
1916 //########################################################################
1923 //########################################################################
1924 //########################################################################
1925 //##  U R I
1926 //########################################################################
1927 //########################################################################
1929 //This would normally be a call to a UNICODE function
1930 #define isLetter(x) isalpha(x)
1932 /**
1933  *  A class that implements the W3C URI resource reference.
1934  */
1935 class URI
1937 public:
1939     typedef enum
1940         {
1941         SCHEME_NONE =0,
1942         SCHEME_DATA,
1943         SCHEME_HTTP,
1944         SCHEME_HTTPS,
1945         SCHEME_FTP,
1946         SCHEME_FILE,
1947         SCHEME_LDAP,
1948         SCHEME_MAILTO,
1949         SCHEME_NEWS,
1950         SCHEME_TELNET
1951         } SchemeTypes;
1953     /**
1954      *
1955      */
1956     URI()
1957         {
1958         init();
1959         }
1961     /**
1962      *
1963      */
1964     URI(const String &str)
1965         {
1966         init();
1967         parse(str);
1968         }
1971     /**
1972      *
1973      */
1974     URI(const char *str)
1975         {
1976         init();
1977         String domStr = str;
1978         parse(domStr);
1979         }
1982     /**
1983      *
1984      */
1985     URI(const URI &other)
1986         {
1987         init();
1988         assign(other);
1989         }
1992     /**
1993      *
1994      */
1995     URI &operator=(const URI &other)
1996         {
1997         init();
1998         assign(other);
1999         return *this;
2000         }
2003     /**
2004      *
2005      */
2006     virtual ~URI()
2007         {}
2011     /**
2012      *
2013      */
2014     virtual bool parse(const String &str);
2016     /**
2017      *
2018      */
2019     virtual String toString() const;
2021     /**
2022      *
2023      */
2024     virtual int getScheme() const;
2026     /**
2027      *
2028      */
2029     virtual String getSchemeStr() const;
2031     /**
2032      *
2033      */
2034     virtual String getAuthority() const;
2036     /**
2037      *  Same as getAuthority, but if the port has been specified
2038      *  as host:port , the port will not be included
2039      */
2040     virtual String getHost() const;
2042     /**
2043      *
2044      */
2045     virtual int getPort() const;
2047     /**
2048      *
2049      */
2050     virtual String getPath() const;
2052     /**
2053      *
2054      */
2055     virtual String getNativePath() const;
2057     /**
2058      *
2059      */
2060     virtual bool isAbsolute() const;
2062     /**
2063      *
2064      */
2065     virtual bool isOpaque() const;
2067     /**
2068      *
2069      */
2070     virtual String getQuery() const;
2072     /**
2073      *
2074      */
2075     virtual String getFragment() const;
2077     /**
2078      *
2079      */
2080     virtual URI resolve(const URI &other) const;
2082     /**
2083      *
2084      */
2085     virtual void normalize();
2087 private:
2089     /**
2090      *
2091      */
2092     void init()
2093         {
2094         parsebuf  = NULL;
2095         parselen  = 0;
2096         scheme    = SCHEME_NONE;
2097         schemeStr = "";
2098         port      = 0;
2099         authority = "";
2100         path      = "";
2101         absolute  = false;
2102         opaque    = false;
2103         query     = "";
2104         fragment  = "";
2105         }
2108     /**
2109      *
2110      */
2111     void assign(const URI &other)
2112         {
2113         scheme    = other.scheme;
2114         schemeStr = other.schemeStr;
2115         authority = other.authority;
2116         port      = other.port;
2117         path      = other.path;
2118         absolute  = other.absolute;
2119         opaque    = other.opaque;
2120         query     = other.query;
2121         fragment  = other.fragment;
2122         }
2124     int scheme;
2126     String schemeStr;
2128     String authority;
2130     bool portSpecified;
2132     int port;
2134     String path;
2136     bool absolute;
2138     bool opaque;
2140     String query;
2142     String fragment;
2144     void error(const char *fmt, ...);
2146     void trace(const char *fmt, ...);
2149     int peek(int p);
2151     int match(int p, char *key);
2153     int parseScheme(int p);
2155     int parseHierarchicalPart(int p0);
2157     int parseQuery(int p0);
2159     int parseFragment(int p0);
2161     int parse(int p);
2163     char *parsebuf;
2165     int parselen;
2167 };
2171 typedef struct
2173     int  ival;
2174     char *sval;
2175     int  port;
2176 } LookupEntry;
2178 LookupEntry schemes[] =
2180     { URI::SCHEME_DATA,   "data:",    0 },
2181     { URI::SCHEME_HTTP,   "http:",   80 },
2182     { URI::SCHEME_HTTPS,  "https:", 443 },
2183     { URI::SCHEME_FTP,    "ftp",     12 },
2184     { URI::SCHEME_FILE,   "file:",    0 },
2185     { URI::SCHEME_LDAP,   "ldap:",  123 },
2186     { URI::SCHEME_MAILTO, "mailto:", 25 },
2187     { URI::SCHEME_NEWS,   "news:",  117 },
2188     { URI::SCHEME_TELNET, "telnet:", 23 },
2189     { 0,                  NULL,       0 }
2190 };
2193 String URI::toString() const
2195     String str = schemeStr;
2196     if (authority.size() > 0)
2197         {
2198         str.append("//");
2199         str.append(authority);
2200         }
2201     str.append(path);
2202     if (query.size() > 0)
2203         {
2204         str.append("?");
2205         str.append(query);
2206         }
2207     if (fragment.size() > 0)
2208         {
2209         str.append("#");
2210         str.append(fragment);
2211         }
2212     return str;
2216 int URI::getScheme() const
2218     return scheme;
2221 String URI::getSchemeStr() const
2223     return schemeStr;
2227 String URI::getAuthority() const
2229     String ret = authority;
2230     if (portSpecified && port>=0)
2231         {
2232         char buf[7];
2233         snprintf(buf, 6, ":%6d", port);
2234         ret.append(buf);
2235         }
2236     return ret;
2239 String URI::getHost() const
2241     return authority;
2244 int URI::getPort() const
2246     return port;
2250 String URI::getPath() const
2252     return path;
2255 String URI::getNativePath() const
2257     String npath;
2258 #ifdef __WIN32__
2259     unsigned int firstChar = 0;
2260     if (path.size() >= 3)
2261         {
2262         if (path[0] == '/' &&
2263             isLetter(path[1]) &&
2264             path[2] == ':')
2265             firstChar++;
2266          }
2267     for (unsigned int i=firstChar ; i<path.size() ; i++)
2268         {
2269         XMLCh ch = (XMLCh) path[i];
2270         if (ch == '/')
2271             npath.push_back((XMLCh)'\\');
2272         else
2273             npath.push_back(ch);
2274         }
2275 #else
2276     npath = path;
2277 #endif
2278     return npath;
2282 bool URI::isAbsolute() const
2284     return absolute;
2287 bool URI::isOpaque() const
2289     return opaque;
2293 String URI::getQuery() const
2295     return query;
2299 String URI::getFragment() const
2301     return fragment;
2305 URI URI::resolve(const URI &other) const
2307     //### According to w3c, this is handled in 3 cases
2309     //## 1
2310     if (opaque || other.isAbsolute())
2311         return other;
2313     //## 2
2314     if (other.fragment.size()  >  0 &&
2315         other.path.size()      == 0 &&
2316         other.scheme           == SCHEME_NONE &&
2317         other.authority.size() == 0 &&
2318         other.query.size()     == 0 )
2319         {
2320         URI fragUri = *this;
2321         fragUri.fragment = other.fragment;
2322         return fragUri;
2323         }
2325     //## 3 http://www.ietf.org/rfc/rfc2396.txt, section 5.2
2326     URI newUri;
2327     //# 3.1
2328     newUri.scheme    = scheme;
2329     newUri.schemeStr = schemeStr;
2330     newUri.query     = other.query;
2331     newUri.fragment  = other.fragment;
2332     if (other.authority.size() > 0)
2333         {
2334         //# 3.2
2335         if (absolute || other.absolute)
2336             newUri.absolute = true;
2337         newUri.authority = other.authority;
2338         newUri.port      = other.port;//part of authority
2339         newUri.path      = other.path;
2340         }
2341     else
2342         {
2343         //# 3.3
2344         if (other.absolute)
2345             {
2346             newUri.absolute = true;
2347             newUri.path     = other.path;
2348             }
2349         else
2350             {
2351             unsigned int pos = path.find_last_of('/');
2352             if (pos != path.npos)
2353                 {
2354                 String tpath = path.substr(0, pos+1);
2355                 tpath.append(other.path);
2356                 newUri.path = tpath;
2357                 }
2358             else
2359                 newUri.path = other.path;
2360             }
2361         }
2363     newUri.normalize();
2364     return newUri;
2369 /**
2370  *  This follows the Java URI algorithm:
2371  *   1. All "." segments are removed.
2372  *   2. If a ".." segment is preceded by a non-".." segment
2373  *          then both of these segments are removed. This step
2374  *          is repeated until it is no longer applicable.
2375  *   3. If the path is relative, and if its first segment
2376  *          contains a colon character (':'), then a "." segment
2377  *          is prepended. This prevents a relative URI with a path
2378  *          such as "a:b/c/d" from later being re-parsed as an
2379  *          opaque URI with a scheme of "a" and a scheme-specific
2380  *          part of "b/c/d". (Deviation from RFC 2396)
2381  */
2382 void URI::normalize()
2384     std::vector<String> segments;
2386     //## Collect segments
2387     if (path.size()<2)
2388         return;
2389     bool abs = false;
2390     unsigned int pos=0;
2391     if (path[0]=='/')
2392         {
2393         abs = true;
2394         pos++;
2395         }
2396     while (pos < path.size())
2397         {
2398         unsigned int pos2 = path.find('/', pos);
2399         if (pos2==path.npos)
2400             {
2401             String seg = path.substr(pos);
2402             //printf("last segment:%s\n", seg.c_str());
2403             segments.push_back(seg);
2404             break;
2405             }
2406         if (pos2>pos)
2407             {
2408             String seg = path.substr(pos, pos2-pos);
2409             //printf("segment:%s\n", seg.c_str());
2410             segments.push_back(seg);
2411             }
2412         pos = pos2;
2413         pos++;
2414         }
2416     //## Clean up (normalize) segments
2417     bool edited = false;
2418     std::vector<String>::iterator iter;
2419     for (iter=segments.begin() ; iter!=segments.end() ; )
2420         {
2421         String s = *iter;
2422         if (s == ".")
2423             {
2424             iter = segments.erase(iter);
2425             edited = true;
2426             }
2427         else if (s == ".." &&
2428                  iter != segments.begin() &&
2429                  *(iter-1) != "..")
2430             {
2431             iter--; //back up, then erase two entries
2432             iter = segments.erase(iter);
2433             iter = segments.erase(iter);
2434             edited = true;
2435             }
2436         else
2437             iter++;
2438         }
2440     //## Rebuild path, if necessary
2441     if (edited)
2442         {
2443         path.clear();
2444         if (abs)
2445             {
2446             path.append("/");
2447             }
2448         std::vector<String>::iterator iter;
2449         for (iter=segments.begin() ; iter!=segments.end() ; iter++)
2450             {
2451             if (iter != segments.begin())
2452                 path.append("/");
2453             path.append(*iter);
2454             }
2455         }
2461 //#########################################################################
2462 //# M E S S A G E S
2463 //#########################################################################
2465 void URI::error(const char *fmt, ...)
2467     va_list args;
2468     fprintf(stderr, "URI error: ");
2469     va_start(args, fmt);
2470     vfprintf(stderr, fmt, args);
2471     va_end(args);
2472     fprintf(stderr, "\n");
2475 void URI::trace(const char *fmt, ...)
2477     va_list args;
2478     fprintf(stdout, "URI: ");
2479     va_start(args, fmt);
2480     vfprintf(stdout, fmt, args);
2481     va_end(args);
2482     fprintf(stdout, "\n");
2488 //#########################################################################
2489 //# P A R S I N G
2490 //#########################################################################
2494 int URI::peek(int p)
2496     if (p<0 || p>=parselen)
2497         return -1;
2498     return parsebuf[p];
2503 int URI::match(int p0, char *key)
2505     int p = p0;
2506     while (p < parselen)
2507         {
2508         if (*key == '\0')
2509             return p;
2510         else if (*key != parsebuf[p])
2511             break;
2512         p++; key++;
2513         }
2514     return p0;
2517 //#########################################################################
2518 //#  Parsing is performed according to:
2519 //#  http://www.gbiv.com/protocols/uri/rfc/rfc3986.html#components
2520 //#########################################################################
2522 int URI::parseScheme(int p0)
2524     int p = p0;
2525     for (LookupEntry *entry = schemes; entry->sval ; entry++)
2526         {
2527         int p2 = match(p, entry->sval);
2528         if (p2 > p)
2529             {
2530             schemeStr = entry->sval;
2531             scheme    = entry->ival;
2532             port      = entry->port;
2533             p = p2;
2534             return p;
2535             }
2536         }
2538     return p;
2542 int URI::parseHierarchicalPart(int p0)
2544     int p = p0;
2545     int ch;
2547     //# Authority field (host and port, for example)
2548     int p2 = match(p, "//");
2549     if (p2 > p)
2550         {
2551         p = p2;
2552         portSpecified = false;
2553         String portStr;
2554         while (p < parselen)
2555             {
2556             ch = peek(p);
2557             if (ch == '/')
2558                 break;
2559             else if (ch == ':')
2560                 portSpecified = true;
2561             else if (portSpecified)
2562                 portStr.push_back((XMLCh)ch);
2563             else
2564                 authority.push_back((XMLCh)ch);
2565             p++;
2566             }
2567         if (portStr.size() > 0)
2568             {
2569             char *pstr = (char *)portStr.c_str();
2570             char *endStr;
2571             long val = strtol(pstr, &endStr, 10);
2572             if (endStr > pstr) //successful parse?
2573                 port = val;
2574             }
2575         }
2577     //# Are we absolute?
2578     ch = peek(p);
2579     if (isLetter(ch) && peek(p+1)==':')
2580         {
2581         absolute = true;
2582         path.push_back((XMLCh)'/');
2583         }
2584     else if (ch == '/')
2585         {
2586         absolute = true;
2587         if (p>p0) //in other words, if '/' is not the first char
2588             opaque = true;
2589         path.push_back((XMLCh)ch);
2590         p++;
2591         }
2593     while (p < parselen)
2594         {
2595         ch = peek(p);
2596         if (ch == '?' || ch == '#')
2597             break;
2598         path.push_back((XMLCh)ch);
2599         p++;
2600         }
2602     return p;
2605 int URI::parseQuery(int p0)
2607     int p = p0;
2608     int ch = peek(p);
2609     if (ch != '?')
2610         return p0;
2612     p++;
2613     while (p < parselen)
2614         {
2615         ch = peek(p);
2616         if (ch == '#')
2617             break;
2618         query.push_back((XMLCh)ch);
2619         p++;
2620         }
2623     return p;
2626 int URI::parseFragment(int p0)
2629     int p = p0;
2630     int ch = peek(p);
2631     if (ch != '#')
2632         return p0;
2634     p++;
2635     while (p < parselen)
2636         {
2637         ch = peek(p);
2638         if (ch == '?')
2639             break;
2640         fragment.push_back((XMLCh)ch);
2641         p++;
2642         }
2645     return p;
2649 int URI::parse(int p0)
2652     int p = p0;
2654     int p2 = parseScheme(p);
2655     if (p2 < 0)
2656         {
2657         error("Scheme");
2658         return -1;
2659         }
2660     p = p2;
2663     p2 = parseHierarchicalPart(p);
2664     if (p2 < 0)
2665         {
2666         error("Hierarchical part");
2667         return -1;
2668         }
2669     p = p2;
2671     p2 = parseQuery(p);
2672     if (p2 < 0)
2673         {
2674         error("Query");
2675         return -1;
2676         }
2677     p = p2;
2680     p2 = parseFragment(p);
2681     if (p2 < 0)
2682         {
2683         error("Fragment");
2684         return -1;
2685         }
2686     p = p2;
2688     return p;
2694 bool URI::parse(const String &str)
2696     init();
2697     
2698     parselen = str.size();
2700     String tmp;
2701     for (unsigned int i=0 ; i<str.size() ; i++)
2702         {
2703         XMLCh ch = (XMLCh) str[i];
2704         if (ch == '\\')
2705             tmp.push_back((XMLCh)'/');
2706         else
2707             tmp.push_back(ch);
2708         }
2709     parsebuf = (char *) tmp.c_str();
2712     int p = parse(0);
2713     normalize();
2715     if (p < 0)
2716         {
2717         error("Syntax error");
2718         return false;
2719         }
2721     //printf("uri:%s\n", toString().c_str());
2722     //printf("path:%s\n", path.c_str());
2724     return true;
2735 //########################################################################
2736 //########################################################################
2737 //##  M A K E
2738 //########################################################################
2739 //########################################################################
2741 //########################################################################
2742 //# F I L E S E T
2743 //########################################################################
2744 /**
2745  * This is the descriptor for a <fileset> item
2746  */
2747 class FileSet
2749 public:
2751     /**
2752      *
2753      */
2754     FileSet()
2755         {}
2757     /**
2758      *
2759      */
2760     FileSet(const FileSet &other)
2761         { assign(other); }
2763     /**
2764      *
2765      */
2766     FileSet &operator=(const FileSet &other)
2767         { assign(other); return *this; }
2769     /**
2770      *
2771      */
2772     virtual ~FileSet()
2773         {}
2775     /**
2776      *
2777      */
2778     String getDirectory()
2779         { return directory; }
2780         
2781     /**
2782      *
2783      */
2784     void setDirectory(const String &val)
2785         { directory = val; }
2787     /**
2788      *
2789      */
2790     void setFiles(const std::vector<String> &val)
2791         { files = val; }
2793     /**
2794      *
2795      */
2796     std::vector<String> getFiles()
2797         { return files; }
2798         
2799     /**
2800      *
2801      */
2802     void setIncludes(const std::vector<String> &val)
2803         { includes = val; }
2805     /**
2806      *
2807      */
2808     std::vector<String> getIncludes()
2809         { return includes; }
2810         
2811     /**
2812      *
2813      */
2814     void setExcludes(const std::vector<String> &val)
2815         { excludes = val; }
2817     /**
2818      *
2819      */
2820     std::vector<String> getExcludes()
2821         { return excludes; }
2822         
2823     /**
2824      *
2825      */
2826     unsigned int size()
2827         { return files.size(); }
2828         
2829     /**
2830      *
2831      */
2832     String operator[](int index)
2833         { return files[index]; }
2834         
2835     /**
2836      *
2837      */
2838     void clear()
2839         {
2840         directory = "";
2841         files.clear();
2842         includes.clear();
2843         excludes.clear();
2844         }
2845         
2847 private:
2849     void assign(const FileSet &other)
2850         {
2851         directory = other.directory;
2852         files     = other.files;
2853         includes  = other.includes;
2854         excludes  = other.excludes;
2855         }
2857     String directory;
2858     std::vector<String> files;
2859     std::vector<String> includes;
2860     std::vector<String> excludes;
2861 };
2866 //########################################################################
2867 //# M A K E    B A S E
2868 //########################################################################
2869 /**
2870  * Base class for all classes in this file
2871  */
2872 class MakeBase
2874 public:
2876     MakeBase()
2877         { line = 0; }
2878     virtual ~MakeBase()
2879         {}
2881     /**
2882      *     Return the URI of the file associated with this object 
2883      */     
2884     URI getURI()
2885         { return uri; }
2887     /**
2888      * Set the uri to the given string
2889      */
2890     void setURI(const String &uristr)
2891         { uri.parse(uristr); }
2893     /**
2894      *  Resolve another path relative to this one
2895      */
2896     String resolve(const String &otherPath);
2898     /**
2899      *  Get an element attribute, performing substitutions if necessary
2900      */
2901     bool getAttribute(Element *elem, const String &name, String &result);
2903     /**
2904      * Get an element value, performing substitutions if necessary
2905      */
2906     bool getValue(Element *elem, String &result);
2907     
2908     /**
2909      * Set the current line number in the file
2910      */         
2911     void setLine(int val)
2912         { line = val; }
2913         
2914     /**
2915      * Get the current line number in the file
2916      */         
2917     int getLine()
2918         { return line; }
2920 protected:
2922     /**
2923      *    The path to the file associated with this object
2924      */     
2925     URI uri;
2928     /**
2929      *  Print a printf()-like formatted error message
2930      */
2931     void error(char *fmt, ...);
2933     /**
2934      *  Print a printf()-like formatted trace message
2935      */
2936     void status(char *fmt, ...);
2938     /**
2939      *  Print a printf()-like formatted trace message
2940      */
2941     void trace(char *fmt, ...);
2943     /**
2944      *  Check if a given string matches a given regex pattern
2945      */
2946     bool regexMatch(const String &str, const String &pattern);
2948     /**
2949      *
2950      */
2951     String getSuffix(const String &fname);
2953     /**
2954      * Break up a string into substrings delimited the characters
2955      * in delimiters.  Null-length substrings are ignored
2956      */  
2957     std::vector<String> tokenize(const String &val,
2958                           const String &delimiters);
2960     /**
2961      *  replace runs of whitespace with a space
2962      */
2963     String strip(const String &s);
2965     /**
2966      *  remove leading whitespace from each line
2967      */
2968     String leftJustify(const String &s);
2970     /**
2971      *  remove leading and trailing whitespace from string
2972      */
2973     String trim(const String &s);
2975     /**
2976      * Return the native format of the canonical
2977      * path which we store
2978      */
2979     String getNativePath(const String &path);
2981     /**
2982      * Execute a shell command.  Outbuf is a ref to a string
2983      * to catch the result.     
2984      */         
2985     bool executeCommand(const String &call,
2986                         const String &inbuf,
2987                         String &outbuf,
2988                         String &errbuf);
2989     /**
2990      * List all directories in a given base and starting directory
2991      * It is usually called like:
2992      *        bool ret = listDirectories("src", "", result);    
2993      */         
2994     bool listDirectories(const String &baseName,
2995                          const String &dirname,
2996                          std::vector<String> &res);
2998     /**
2999      * Find all files in the named directory 
3000      */         
3001     bool listFiles(const String &baseName,
3002                    const String &dirname,
3003                    std::vector<String> &result);
3005     /**
3006      * Perform a listing for a fileset 
3007      */         
3008     bool listFiles(MakeBase &propRef, FileSet &fileSet);
3010     /**
3011      * Parse a <patternset>
3012      */  
3013     bool parsePatternSet(Element *elem,
3014                        MakeBase &propRef,
3015                        std::vector<String> &includes,
3016                        std::vector<String> &excludes);
3018     /**
3019      * Parse a <fileset> entry, and determine which files
3020      * should be included
3021      */  
3022     bool parseFileSet(Element *elem,
3023                     MakeBase &propRef,
3024                     FileSet &fileSet);
3026     /**
3027      * Return this object's property list
3028      */
3029     virtual std::map<String, String> &getProperties()
3030         { return properties; }
3032     /**
3033      * Return a named property if found, else a null string
3034      */
3035     virtual String getProperty(const String &name)
3036         {
3037         String val;
3038         std::map<String, String>::iterator iter;
3039         iter = properties.find(name);
3040         if (iter != properties.end())
3041             val = iter->second;
3042         return val;
3043         }
3046     std::map<String, String> properties;
3048     /**
3049      * Turn 'true' and 'false' into boolean values
3050      */             
3051     bool getBool(const String &str, bool &val);
3053     /**
3054      * Create a directory, making intermediate dirs
3055      * if necessary
3056      */                  
3057     bool createDirectory(const String &dirname);
3059     /**
3060      * Delete a directory and its children if desired
3061      */
3062     bool removeDirectory(const String &dirName);
3064     /**
3065      * Copy a file from one name to another. Perform only if needed
3066      */ 
3067     bool copyFile(const String &srcFile, const String &destFile);
3069     /**
3070      * Tests if the file exists and is a regular file
3071      */ 
3072     bool isRegularFile(const String &fileName);
3074     /**
3075      * Tests if the file exists and is a directory
3076      */ 
3077     bool isDirectory(const String &fileName);
3079     /**
3080      * Tests is the modification date of fileA is newer than fileB
3081      */ 
3082     bool isNewerThan(const String &fileA, const String &fileB);
3084 private:
3086     /**
3087      * replace variable refs like ${a} with their values
3088      */         
3089     bool getSubstitutions(const String &s, String &result);
3091     int line;
3094 };
3099 /**
3100  *  Print a printf()-like formatted error message
3101  */
3102 void MakeBase::error(char *fmt, ...)
3104     va_list args;
3105     va_start(args,fmt);
3106     fprintf(stderr, "Make error line %d: ", line);
3107     vfprintf(stderr, fmt, args);
3108     fprintf(stderr, "\n");
3109     va_end(args) ;
3114 /**
3115  *  Print a printf()-like formatted trace message
3116  */
3117 void MakeBase::status(char *fmt, ...)
3119     va_list args;
3120     va_start(args,fmt);
3121     //fprintf(stdout, " ");
3122     vfprintf(stdout, fmt, args);
3123     fprintf(stdout, "\n");
3124     va_end(args) ;
3129 /**
3130  *  Resolve another path relative to this one
3131  */
3132 String MakeBase::resolve(const String &otherPath)
3134     URI otherURI(otherPath);
3135     URI fullURI = uri.resolve(otherURI);
3136     String ret = fullURI.toString();
3137     return ret;
3141 /**
3142  *  Print a printf()-like formatted trace message
3143  */
3144 void MakeBase::trace(char *fmt, ...)
3146     va_list args;
3147     va_start(args,fmt);
3148     fprintf(stdout, "Make: ");
3149     vfprintf(stdout, fmt, args);
3150     fprintf(stdout, "\n");
3151     va_end(args) ;
3156 /**
3157  *  Check if a given string matches a given regex pattern
3158  */
3159 bool MakeBase::regexMatch(const String &str, const String &pattern)
3161     const TRexChar *terror = NULL;
3162     const TRexChar *cpat = pattern.c_str();
3163     TRex *expr = trex_compile(cpat, &terror);
3164     if (!expr)
3165         {
3166         if (!terror)
3167             terror = "undefined";
3168         error("compilation error [%s]!\n", terror);
3169         return false;
3170         } 
3172     bool ret = true;
3174     const TRexChar *cstr = str.c_str();
3175     if (trex_match(expr, cstr))
3176         {
3177         ret = true;
3178         }
3179     else
3180         {
3181         ret = false;
3182         }
3184     trex_free(expr);
3186     return ret;
3189 /**
3190  *  Return the suffix, if any, of a file name
3191  */
3192 String MakeBase::getSuffix(const String &fname)
3194     if (fname.size() < 2)
3195         return "";
3196     unsigned int pos = fname.find_last_of('.');
3197     if (pos == fname.npos)
3198         return "";
3199     pos++;
3200     String res = fname.substr(pos, fname.size()-pos);
3201     //trace("suffix:%s", res.c_str()); 
3202     return res;
3207 /**
3208  * Break up a string into substrings delimited the characters
3209  * in delimiters.  Null-length substrings are ignored
3210  */  
3211 std::vector<String> MakeBase::tokenize(const String &str,
3212                                 const String &delimiters)
3215     std::vector<String> res;
3216     char *del = (char *)delimiters.c_str();
3217     String dmp;
3218     for (unsigned int i=0 ; i<str.size() ; i++)
3219         {
3220         char ch = str[i];
3221         char *p = (char *)0;
3222         for (p=del ; *p ; p++)
3223             if (*p == ch)
3224                 break;
3225         if (*p)
3226             {
3227             if (dmp.size() > 0)
3228                 {
3229                 res.push_back(dmp);
3230                 dmp.clear();
3231                 }
3232             }
3233         else
3234             {
3235             dmp.push_back(ch);
3236             }
3237         }
3238     //Add tail
3239     if (dmp.size() > 0)
3240         {
3241         res.push_back(dmp);
3242         dmp.clear();
3243         }
3245     return res;
3250 /**
3251  *  replace runs of whitespace with a single space
3252  */
3253 String MakeBase::strip(const String &s)
3255     int len = s.size();
3256     String stripped;
3257     for (int i = 0 ; i<len ; i++)
3258         {
3259         char ch = s[i];
3260         if (isspace(ch))
3261             {
3262             stripped.push_back(' ');
3263             for ( ; i<len ; i++)
3264                 {
3265                 ch = s[i];
3266                 if (!isspace(ch))
3267                     {
3268                     stripped.push_back(ch);
3269                     break;
3270                     }
3271                 }
3272             }
3273         else
3274             {
3275             stripped.push_back(ch);
3276             }
3277         }
3278     return stripped;
3281 /**
3282  *  remove leading whitespace from each line
3283  */
3284 String MakeBase::leftJustify(const String &s)
3286     String out;
3287     int len = s.size();
3288     for (int i = 0 ; i<len ; )
3289         {
3290         char ch;
3291         //Skip to first visible character
3292         while (i<len)
3293             {
3294             ch = s[i];
3295             if (ch == '\n' || ch == '\r'
3296               || !isspace(ch))
3297                   break;
3298             i++;
3299             }
3300         //Copy the rest of the line
3301         while (i<len)
3302             {
3303             ch = s[i];
3304             if (ch == '\n' || ch == '\r')
3305                 {
3306                 if (ch != '\r')
3307                     out.push_back('\n');
3308                 i++;
3309                 break;
3310                 }
3311             else
3312                 {
3313                 out.push_back(ch);
3314                 }
3315             i++;
3316             }
3317         }
3318     return out;
3322 /**
3323  *  Removes whitespace from beginning and end of a string
3324  */
3325 String MakeBase::trim(const String &s)
3327     if (s.size() < 1)
3328         return s;
3329     
3330     //Find first non-ws char
3331     unsigned int begin = 0;
3332     for ( ; begin < s.size() ; begin++)
3333         {
3334         if (!isspace(s[begin]))
3335             break;
3336         }
3338     //Find first non-ws char, going in reverse
3339     unsigned int end = s.size() - 1;
3340     for ( ; end > begin ; end--)
3341         {
3342         if (!isspace(s[end]))
3343             break;
3344         }
3345     //trace("begin:%d  end:%d", begin, end);
3347     String res = s.substr(begin, end-begin+1);
3348     return res;
3351 /**
3352  * Return the native format of the canonical
3353  * path which we store
3354  */
3355 String MakeBase::getNativePath(const String &path)
3357 #ifdef __WIN32__
3358     String npath;
3359     unsigned int firstChar = 0;
3360     if (path.size() >= 3)
3361         {
3362         if (path[0] == '/' &&
3363             isalpha(path[1]) &&
3364             path[2] == ':')
3365             firstChar++;
3366         }
3367     for (unsigned int i=firstChar ; i<path.size() ; i++)
3368         {
3369         char ch = path[i];
3370         if (ch == '/')
3371             npath.push_back('\\');
3372         else
3373             npath.push_back(ch);
3374         }
3375     return npath;
3376 #else
3377     return path;
3378 #endif
3382 #ifdef __WIN32__
3383 #include <tchar.h>
3385 static String win32LastError()
3388     DWORD dw = GetLastError(); 
3390     LPVOID str;
3391     FormatMessage(
3392         FORMAT_MESSAGE_ALLOCATE_BUFFER | 
3393         FORMAT_MESSAGE_FROM_SYSTEM,
3394         NULL,
3395         dw,
3396         0,
3397         (LPTSTR) &str,
3398         0, NULL );
3399     LPTSTR p = _tcschr((const char *)str, _T('\r'));
3400     if(p != NULL)
3401         { // lose CRLF
3402         *p = _T('\0');
3403         }
3404     String ret = (char *)str;
3405     LocalFree(str);
3407     return ret;
3409 #endif
3413 /**
3414  * Execute a system call, using pipes to send data to the
3415  * program's stdin,  and reading stdout and stderr.
3416  */
3417 bool MakeBase::executeCommand(const String &command,
3418                               const String &inbuf,
3419                               String &outbuf,
3420                               String &errbuf)
3423     status("============ cmd ============\n%s\n=============================",
3424                 command.c_str());
3426     outbuf.clear();
3427     errbuf.clear();
3428     
3429 #ifdef __WIN32__
3431     /*
3432     I really hate having win32 code in this program, but the
3433     read buffer in command.com and cmd.exe are just too small
3434     for the large commands we need for compiling and linking.
3435     */
3437     bool ret = true;
3439     //# Allocate a separate buffer for safety
3440     char *paramBuf = new char[command.size() + 1];
3441     if (!paramBuf)
3442        {
3443        error("executeCommand cannot allocate command buffer");
3444        return false;
3445        }
3446     strcpy(paramBuf, (char *)command.c_str());
3448     //# Go to http://msdn2.microsoft.com/en-us/library/ms682499.aspx
3449     //# to see how Win32 pipes work
3451     //# Create pipes
3452     SECURITY_ATTRIBUTES saAttr; 
3453     saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); 
3454     saAttr.bInheritHandle = TRUE; 
3455     saAttr.lpSecurityDescriptor = NULL; 
3456     HANDLE stdinRead,  stdinWrite;
3457     HANDLE stdoutRead, stdoutWrite;
3458     HANDLE stderrRead, stderrWrite;
3459     if (!CreatePipe(&stdinRead, &stdinWrite, &saAttr, 0))
3460         {
3461         error("executeProgram: could not create pipe");
3462         delete[] paramBuf;
3463         return false;
3464         } 
3465     SetHandleInformation(stdinWrite, HANDLE_FLAG_INHERIT, 0);
3466     if (!CreatePipe(&stdoutRead, &stdoutWrite, &saAttr, 0))
3467         {
3468         error("executeProgram: could not create pipe");
3469         delete[] paramBuf;
3470         return false;
3471         } 
3472     SetHandleInformation(stdoutRead, HANDLE_FLAG_INHERIT, 0);
3473     if (!CreatePipe(&stderrRead, &stderrWrite, &saAttr, 0))
3474         {
3475         error("executeProgram: could not create pipe");
3476         delete[] paramBuf;
3477         return false;
3478         } 
3479     SetHandleInformation(stderrRead, HANDLE_FLAG_INHERIT, 0);
3481     // Create the process
3482     STARTUPINFO siStartupInfo;
3483     PROCESS_INFORMATION piProcessInfo;
3484     memset(&siStartupInfo, 0, sizeof(siStartupInfo));
3485     memset(&piProcessInfo, 0, sizeof(piProcessInfo));
3486     siStartupInfo.cb = sizeof(siStartupInfo);
3487     siStartupInfo.hStdError   =  stderrWrite;
3488     siStartupInfo.hStdOutput  =  stdoutWrite;
3489     siStartupInfo.hStdInput   =  stdinRead;
3490     siStartupInfo.dwFlags    |=  STARTF_USESTDHANDLES;
3491    
3492     if (!CreateProcess(NULL, paramBuf, NULL, NULL, true,
3493                 0, NULL, NULL, &siStartupInfo,
3494                 &piProcessInfo))
3495         {
3496         error("executeCommand : could not create process : %s",
3497                     win32LastError().c_str());
3498         ret = false;
3499         }
3501     delete[] paramBuf;
3503     DWORD bytesWritten;
3504     if (inbuf.size()>0 &&
3505         !WriteFile(stdinWrite, inbuf.c_str(), inbuf.size(), 
3506                &bytesWritten, NULL))
3507         {
3508         error("executeCommand: could not write to pipe");
3509         return false;
3510         }    
3511     if (!CloseHandle(stdinWrite))
3512         {          
3513         error("executeCommand: could not close write pipe");
3514         return false;
3515         }
3516     if (!CloseHandle(stdoutWrite))
3517         {
3518         error("executeCommand: could not close read pipe");
3519         return false;
3520         }
3521     if (!CloseHandle(stderrWrite))
3522         {
3523         error("executeCommand: could not close read pipe");
3524         return false;
3525         }
3527     bool lastLoop = false;
3528     while (true)
3529         {
3530         DWORD avail;
3531         DWORD bytesRead;
3532         char readBuf[4096];
3534         //trace("## stderr");
3535         PeekNamedPipe(stderrRead, NULL, 0, NULL, &avail, NULL);
3536         if (avail > 0)
3537             {
3538             bytesRead = 0;
3539             if (avail>4096) avail = 4096;
3540             ReadFile(stderrRead, readBuf, avail, &bytesRead, NULL);
3541             if (bytesRead > 0)
3542                 {
3543                 for (unsigned int i=0 ; i<bytesRead ; i++)
3544                     errbuf.push_back(readBuf[i]);
3545                 }
3546             }
3548         //trace("## stdout");
3549         PeekNamedPipe(stdoutRead, NULL, 0, NULL, &avail, NULL);
3550         if (avail > 0)
3551             {
3552             bytesRead = 0;
3553             if (avail>4096) avail = 4096;
3554             ReadFile(stdoutRead, readBuf, avail, &bytesRead, NULL);
3555             if (bytesRead > 0)
3556                 {
3557                 for (unsigned int i=0 ; i<bytesRead ; i++)
3558                     outbuf.push_back(readBuf[i]);
3559                 }
3560             }
3561             
3562         //Was this the final check after program done?
3563         if (lastLoop)
3564             break;
3566         DWORD exitCode;
3567         GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
3568         if (exitCode != STILL_ACTIVE)
3569             lastLoop = true;
3571         Sleep(10);
3572         }    
3573     //trace("outbuf:%s", outbuf.c_str());
3574     if (!CloseHandle(stdoutRead))
3575         {
3576         error("executeCommand: could not close read pipe");
3577         return false;
3578         }
3579     if (!CloseHandle(stderrRead))
3580         {
3581         error("executeCommand: could not close read pipe");
3582         return false;
3583         }
3585     DWORD exitCode;
3586     GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
3587     //trace("exit code:%d", exitCode);
3588     if (exitCode != 0)
3589         {
3590         ret = false;
3591         }
3592     
3593     CloseHandle(piProcessInfo.hProcess);
3594     CloseHandle(piProcessInfo.hThread);
3596     return ret;
3598 #else //do it unix-style
3600     String s;
3601     FILE *f = popen(command.c_str(), "r");
3602     int errnum = 0;
3603     if (f)
3604         {
3605         while (true)
3606             {
3607             int ch = fgetc(f);
3608             if (ch < 0)
3609                 break;
3610             s.push_back((char)ch);
3611             }
3612         errnum = pclose(f);
3613         }
3614     outbuf = s;
3615     if (errnum != 0)
3616         {
3617         error("exec of command '%s' failed : %s",
3618              command.c_str(), strerror(errno));
3619         return false;
3620         }
3621     else
3622         return true;
3624 #endif
3625
3630 bool MakeBase::listDirectories(const String &baseName,
3631                               const String &dirName,
3632                               std::vector<String> &res)
3634     res.push_back(dirName);
3635     String fullPath = baseName;
3636     if (dirName.size()>0)
3637         {
3638         fullPath.append("/");
3639         fullPath.append(dirName);
3640         }
3641     DIR *dir = opendir(fullPath.c_str());
3642     while (true)
3643         {
3644         struct dirent *de = readdir(dir);
3645         if (!de)
3646             break;
3648         //Get the directory member name
3649         String s = de->d_name;
3650         if (s.size() == 0 || s[0] == '.')
3651             continue;
3652         String childName = dirName;
3653         childName.append("/");
3654         childName.append(s);
3656         String fullChildPath = baseName;
3657         fullChildPath.append("/");
3658         fullChildPath.append(childName);
3659         struct stat finfo;
3660         String childNative = getNativePath(fullChildPath);
3661         if (stat(childNative.c_str(), &finfo)<0)
3662             {
3663             error("cannot stat file:%s", childNative.c_str());
3664             }
3665         else if (S_ISDIR(finfo.st_mode))
3666             {
3667             //trace("directory: %s", childName.c_str());
3668             if (!listDirectories(baseName, childName, res))
3669                 return false;
3670             }
3671         }
3672     closedir(dir);
3674     return true;
3678 bool MakeBase::listFiles(const String &baseDir,
3679                          const String &dirName,
3680                          std::vector<String> &res)
3682     String fullDir = baseDir;
3683     if (dirName.size()>0)
3684         {
3685         fullDir.append("/");
3686         fullDir.append(dirName);
3687         }
3688     String dirNative = getNativePath(fullDir);
3690     std::vector<String> subdirs;
3691     DIR *dir = opendir(dirNative.c_str());
3692     if (!dir)
3693         {
3694         error("Could not open directory %s : %s",
3695               dirNative.c_str(), strerror(errno));
3696         return false;
3697         }
3698     while (true)
3699         {
3700         struct dirent *de = readdir(dir);
3701         if (!de)
3702             break;
3704         //Get the directory member name
3705         String s = de->d_name;
3706         if (s.size() == 0 || s[0] == '.')
3707             continue;
3708         String childName;
3709         if (dirName.size()>0)
3710             {
3711             childName.append(dirName);
3712             childName.append("/");
3713             }
3714         childName.append(s);
3715         String fullChild = baseDir;
3716         fullChild.append("/");
3717         fullChild.append(childName);
3718         
3719         if (isDirectory(fullChild))
3720             {
3721             //trace("directory: %s", childName.c_str());
3722             if (!listFiles(baseDir, childName, res))
3723                 return false;
3724             continue;
3725             }
3726         else if (!isRegularFile(fullChild))
3727             {
3728             error("unknown file:%s", childName.c_str());
3729             return false;
3730             }
3732        //all done!
3733         res.push_back(childName);
3735         }
3736     closedir(dir);
3738     return true;
3742 bool MakeBase::listFiles(MakeBase &propRef, FileSet &fileSet)
3744     String baseDir = propRef.resolve(fileSet.getDirectory());
3745     std::vector<String> fileList;
3746     if (!listFiles(baseDir, "", fileList))
3747         return false;
3749     std::vector<String> includes = fileSet.getIncludes();
3750     std::vector<String> excludes = fileSet.getExcludes();
3752     std::vector<String> incs;
3753     std::vector<String>::iterator iter;
3755     std::sort(fileList.begin(), fileList.end());
3757     //If there are <includes>, then add files to the output
3758     //in the order of the include list
3759     if (includes.size()==0)
3760         incs = fileList;
3761     else
3762         {
3763         for (iter = includes.begin() ; iter != includes.end() ; iter++)
3764             {
3765             String pattern = *iter;
3766             std::vector<String>::iterator siter;
3767             for (siter = fileList.begin() ; siter != fileList.end() ; siter++)
3768                 {
3769                 String s = *siter;
3770                 if (regexMatch(s, pattern))
3771                     {
3772                     //trace("INCLUDED:%s", s.c_str());
3773                     incs.push_back(s);
3774                     }
3775                 }
3776             }
3777         }
3779     //Now trim off the <excludes>
3780     std::vector<String> res;
3781     for (iter = incs.begin() ; iter != incs.end() ; iter++)
3782         {
3783         String s = *iter;
3784         bool skipme = false;
3785         std::vector<String>::iterator siter;
3786         for (siter = excludes.begin() ; siter != excludes.end() ; siter++)
3787             {
3788             String pattern = *siter;
3789             if (regexMatch(s, pattern))
3790                 {
3791                 //trace("EXCLUDED:%s", s.c_str());
3792                 skipme = true;
3793                 break;
3794                 }
3795             }
3796         if (!skipme)
3797             res.push_back(s);
3798         }
3799         
3800     fileSet.setFiles(res);
3802     return true;
3809 bool MakeBase::getSubstitutions(const String &str, String &result)
3811     String s = trim(str);
3812     int len = (int)s.size();
3813     String val;
3814     for (int i=0 ; i<len ; i++)
3815         {
3816         char ch = s[i];
3817         if (ch == '$' && s[i+1] == '{')
3818             {
3819             String varname;
3820             int j = i+2;
3821             for ( ; j<len ; j++)
3822                 {
3823                 ch = s[j];
3824                 if (ch == '$' && s[j+1] == '{')
3825                     {
3826                     error("attribute %s cannot have nested variable references",
3827                            s.c_str());
3828                     return false;
3829                     }
3830                 else if (ch == '}')
3831                     {
3832                     std::map<String, String>::iterator iter;
3833                     iter = properties.find(trim(varname));
3834                     if (iter != properties.end())
3835                         {
3836                         val.append(iter->second);
3837                         }
3838                     else
3839                         {
3840                         error("property ${%s} not found", varname.c_str());
3841                         return false;
3842                         }
3843                     break;
3844                     }
3845                 else
3846                     {
3847                     varname.push_back(ch);
3848                     }
3849                 }
3850             i = j;
3851             }
3852         else
3853             {
3854             val.push_back(ch);
3855             }
3856         }
3857     result = val;
3858     return true;
3862 bool MakeBase::getAttribute(Element *elem, const String &name,
3863                                     String &result)
3865     String s = elem->getAttribute(name);
3866     return getSubstitutions(s, result);
3870 bool MakeBase::getValue(Element *elem, String &result)
3872     String s = elem->getValue();
3873     //Replace all runs of whitespace with a single space
3874     return getSubstitutions(s, result);
3878 /**
3879  * Turn 'true' and 'false' into boolean values
3880  */             
3881 bool MakeBase::getBool(const String &str, bool &val)
3883     if (str == "true")
3884         val = true;
3885     else if (str == "false")
3886         val = false;
3887     else
3888         {
3889         error("expected 'true' or 'false'.  found '%s'", str.c_str());
3890         return false;
3891         }
3892     return true;
3898 /**
3899  * Parse a <patternset> entry
3900  */  
3901 bool MakeBase::parsePatternSet(Element *elem,
3902                           MakeBase &propRef,
3903                           std::vector<String> &includes,
3904                           std::vector<String> &excludes
3905                           )
3907     std::vector<Element *> children  = elem->getChildren();
3908     for (unsigned int i=0 ; i<children.size() ; i++)
3909         {
3910         Element *child = children[i];
3911         String tagName = child->getName();
3912         if (tagName == "exclude")
3913             {
3914             String fname;
3915             if (!propRef.getAttribute(child, "name", fname))
3916                 return false;
3917             //trace("EXCLUDE: %s", fname.c_str());
3918             excludes.push_back(fname);
3919             }
3920         else if (tagName == "include")
3921             {
3922             String fname;
3923             if (!propRef.getAttribute(child, "name", fname))
3924                 return false;
3925             //trace("INCLUDE: %s", fname.c_str());
3926             includes.push_back(fname);
3927             }
3928         }
3930     return true;
3936 /**
3937  * Parse a <fileset> entry, and determine which files
3938  * should be included
3939  */  
3940 bool MakeBase::parseFileSet(Element *elem,
3941                           MakeBase &propRef,
3942                           FileSet &fileSet)
3944     String name = elem->getName();
3945     if (name != "fileset")
3946         {
3947         error("expected <fileset>");
3948         return false;
3949         }
3952     std::vector<String> includes;
3953     std::vector<String> excludes;
3955     //A fileset has one implied patternset
3956     if (!parsePatternSet(elem, propRef, includes, excludes))
3957         {
3958         return false;
3959         }
3960     //Look for child tags, including more patternsets
3961     std::vector<Element *> children  = elem->getChildren();
3962     for (unsigned int i=0 ; i<children.size() ; i++)
3963         {
3964         Element *child = children[i];
3965         String tagName = child->getName();
3966         if (tagName == "patternset")
3967             {
3968             if (!parsePatternSet(child, propRef, includes, excludes))
3969                 {
3970                 return false;
3971                 }
3972             }
3973         }
3975     String dir;
3976     //Now do the stuff
3977     //Get the base directory for reading file names
3978     if (!propRef.getAttribute(elem, "dir", dir))
3979         return false;
3981     fileSet.setDirectory(dir);
3982     fileSet.setIncludes(includes);
3983     fileSet.setExcludes(excludes);
3984     
3985     /*
3986     std::vector<String> fileList;
3987     if (dir.size() > 0)
3988         {
3989         String baseDir = propRef.resolve(dir);
3990         if (!listFiles(baseDir, "", includes, excludes, fileList))
3991             return false;
3992         }
3993     std::sort(fileList.begin(), fileList.end());
3994     result = fileList;
3995     */
3997     
3998     /*
3999     for (unsigned int i=0 ; i<result.size() ; i++)
4000         {
4001         trace("RES:%s", result[i].c_str());
4002         }
4003     */
4005     
4006     return true;
4011 /**
4012  * Create a directory, making intermediate dirs
4013  * if necessary
4014  */                  
4015 bool MakeBase::createDirectory(const String &dirname)
4017     //trace("## createDirectory: %s", dirname.c_str());
4018     //## first check if it exists
4019     struct stat finfo;
4020     String nativeDir = getNativePath(dirname);
4021     char *cnative = (char *) nativeDir.c_str();
4022 #ifdef __WIN32__
4023     if (strlen(cnative)==2 && cnative[1]==':')
4024         return true;
4025 #endif
4026     if (stat(cnative, &finfo)==0)
4027         {
4028         if (!S_ISDIR(finfo.st_mode))
4029             {
4030             error("mkdir: file %s exists but is not a directory",
4031                   cnative);
4032             return false;
4033             }
4034         else //exists
4035             {
4036             return true;
4037             }
4038         }
4040     //## 2: pull off the last path segment, if any,
4041     //## to make the dir 'above' this one, if necessary
4042     unsigned int pos = dirname.find_last_of('/');
4043     if (pos>0 && pos != dirname.npos)
4044         {
4045         String subpath = dirname.substr(0, pos);
4046         //A letter root (c:) ?
4047         if (!createDirectory(subpath))
4048             return false;
4049         }
4050         
4051     //## 3: now make
4052 #ifdef __WIN32__
4053     if (mkdir(cnative)<0)
4054 #else
4055     if (mkdir(cnative, S_IRWXU | S_IRWXG | S_IRWXO)<0)
4056 #endif
4057         {
4058         error("cannot make directory '%s' : %s",
4059                  cnative, strerror(errno));
4060         return false;
4061         }
4062         
4063     return true;
4067 /**
4068  * Remove a directory recursively
4069  */ 
4070 bool MakeBase::removeDirectory(const String &dirName)
4072     char *dname = (char *)dirName.c_str();
4074     DIR *dir = opendir(dname);
4075     if (!dir)
4076         {
4077         //# Let this fail nicely.
4078         return true;
4079         //error("error opening directory %s : %s", dname, strerror(errno));
4080         //return false;
4081         }
4082     
4083     while (true)
4084         {
4085         struct dirent *de = readdir(dir);
4086         if (!de)
4087             break;
4089         //Get the directory member name
4090         String s = de->d_name;
4091         if (s.size() == 0 || s[0] == '.')
4092             continue;
4093         String childName;
4094         if (dirName.size() > 0)
4095             {
4096             childName.append(dirName);
4097             childName.append("/");
4098             }
4099         childName.append(s);
4102         struct stat finfo;
4103         String childNative = getNativePath(childName);
4104         char *cnative = (char *)childNative.c_str();
4105         if (stat(cnative, &finfo)<0)
4106             {
4107             error("cannot stat file:%s", cnative);
4108             }
4109         else if (S_ISDIR(finfo.st_mode))
4110             {
4111             //trace("DEL dir: %s", childName.c_str());
4112             if (!removeDirectory(childName))
4113                 {
4114                 return false;
4115                 }
4116             }
4117         else if (!S_ISREG(finfo.st_mode))
4118             {
4119             //trace("not regular: %s", cnative);
4120             }
4121         else
4122             {
4123             //trace("DEL file: %s", childName.c_str());
4124             if (remove(cnative)<0)
4125                 {
4126                 error("error deleting %s : %s",
4127                      cnative, strerror(errno));
4128                 return false;
4129                 }
4130             }
4131         }
4132     closedir(dir);
4134     //Now delete the directory
4135     String native = getNativePath(dirName);
4136     if (rmdir(native.c_str())<0)
4137         {
4138         error("could not delete directory %s : %s",
4139             native.c_str() , strerror(errno));
4140         return false;
4141         }
4143     return true;
4144     
4148 /**
4149  * Copy a file from one name to another. Perform only if needed
4150  */ 
4151 bool MakeBase::copyFile(const String &srcFile, const String &destFile)
4153     //# 1 Check up-to-date times
4154     String srcNative = getNativePath(srcFile);
4155     struct stat srcinfo;
4156     if (stat(srcNative.c_str(), &srcinfo)<0)
4157         {
4158         error("source file %s for copy does not exist",
4159                  srcNative.c_str());
4160         return false;
4161         }
4163     String destNative = getNativePath(destFile);
4164     struct stat destinfo;
4165     if (stat(destNative.c_str(), &destinfo)==0)
4166         {
4167         if (destinfo.st_mtime >= srcinfo.st_mtime)
4168             return true;
4169         }
4170         
4171     //# 2 prepare a destination directory if necessary
4172     unsigned int pos = destFile.find_last_of('/');
4173     if (pos != destFile.npos)
4174         {
4175         String subpath = destFile.substr(0, pos);
4176         if (!createDirectory(subpath))
4177             return false;
4178         }
4180     //# 3 do the data copy
4181 #ifndef __WIN32__
4183     FILE *srcf = fopen(srcNative.c_str(), "rb");
4184     if (!srcf)
4185         {
4186         error("copyFile cannot open '%s' for reading", srcNative.c_str());
4187         return false;
4188         }
4189     FILE *destf = fopen(destNative.c_str(), "wb");
4190     if (!destf)
4191         {
4192         error("copyFile cannot open %s for writing", srcNative.c_str());
4193         return false;
4194         }
4196     while (!feof(srcf))
4197         {
4198         int ch = fgetc(srcf);
4199         if (ch<0)
4200             break;
4201         fputc(ch, destf);
4202         }
4204     fclose(destf);
4205     fclose(srcf);
4207 #else
4208     
4209     if (!CopyFile(srcNative.c_str(), destNative.c_str(), false))
4210         {
4211         error("copyFile from %s to %s failed",
4212              srcNative.c_str(), destNative.c_str());
4213         return false;
4214         }
4215         
4216 #endif /* __WIN32__ */
4219     return true;
4224 /**
4225  * Tests if the file exists and is a regular file
4226  */ 
4227 bool MakeBase::isRegularFile(const String &fileName)
4229     String native = getNativePath(fileName);
4230     struct stat finfo;
4231     
4232     //Exists?
4233     if (stat(native.c_str(), &finfo)<0)
4234         return false;
4237     //check the file mode
4238     if (!S_ISREG(finfo.st_mode))
4239         return false;
4241     return true;
4244 /**
4245  * Tests if the file exists and is a directory
4246  */ 
4247 bool MakeBase::isDirectory(const String &fileName)
4249     String native = getNativePath(fileName);
4250     struct stat finfo;
4251     
4252     //Exists?
4253     if (stat(native.c_str(), &finfo)<0)
4254         return false;
4257     //check the file mode
4258     if (!S_ISDIR(finfo.st_mode))
4259         return false;
4261     return true;
4266 /**
4267  * Tests is the modification of fileA is newer than fileB
4268  */ 
4269 bool MakeBase::isNewerThan(const String &fileA, const String &fileB)
4271     //trace("isNewerThan:'%s' , '%s'", fileA.c_str(), fileB.c_str());
4272     String nativeA = getNativePath(fileA);
4273     struct stat infoA;
4274     //IF source does not exist, NOT newer
4275     if (stat(nativeA.c_str(), &infoA)<0)
4276         {
4277         return false;
4278         }
4280     String nativeB = getNativePath(fileB);
4281     struct stat infoB;
4282     //IF dest does not exist, YES, newer
4283     if (stat(nativeB.c_str(), &infoB)<0)
4284         {
4285         return true;
4286         }
4288     //check the actual times
4289     if (infoA.st_mtime > infoB.st_mtime)
4290         {
4291         return true;
4292         }
4294     return false;
4298 //########################################################################
4299 //# P K G    C O N F I G
4300 //########################################################################
4302 /**
4303  *
4304  */
4305 class PkgConfig : public MakeBase
4308 public:
4310     /**
4311      *
4312      */
4313     PkgConfig()
4314         { init(); }
4316     /**
4317      *
4318      */
4319     PkgConfig(const String &namearg)
4320         { init(); name = namearg; }
4322     /**
4323      *
4324      */
4325     PkgConfig(const PkgConfig &other)
4326         { assign(other); }
4328     /**
4329      *
4330      */
4331     PkgConfig &operator=(const PkgConfig &other)
4332         { assign(other); return *this; }
4334     /**
4335      *
4336      */
4337     virtual ~PkgConfig()
4338         { }
4340     /**
4341      *
4342      */
4343     virtual String getName()
4344         { return name; }
4346     /**
4347      *
4348      */
4349     virtual String getDescription()
4350         { return description; }
4352     /**
4353      *
4354      */
4355     virtual String getCflags()
4356         { return cflags; }
4358     /**
4359      *
4360      */
4361     virtual String getLibs()
4362         { return libs; }
4364     /**
4365      *
4366      */
4367     virtual String getVersion()
4368         { return version; }
4370     /**
4371      *
4372      */
4373     virtual int getMajorVersion()
4374         { return majorVersion; }
4376     /**
4377      *
4378      */
4379     virtual int getMinorVersion()
4380         { return minorVersion; }
4382     /**
4383      *
4384      */
4385     virtual int getMicroVersion()
4386         { return microVersion; }
4388     /**
4389      *
4390      */
4391     virtual std::map<String, String> &getAttributes()
4392         { return attrs; }
4394     /**
4395      *
4396      */
4397     virtual std::vector<String> &getRequireList()
4398         { return requireList; }
4400     virtual bool readFile(const String &fileName);
4402 private:
4404     void init()
4405         {
4406         name         = "";
4407         description  = "";
4408         cflags       = "";
4409         libs         = "";
4410         requires     = "";
4411         version      = "";
4412         majorVersion = 0;
4413         minorVersion = 0;
4414         microVersion = 0;
4415         fileName     = "";
4416         attrs.clear();
4417         requireList.clear();
4418         }
4420     void assign(const PkgConfig &other)
4421         {
4422         name         = other.name;
4423         description  = other.description;
4424         cflags       = other.cflags;
4425         libs         = other.libs;
4426         requires     = other.requires;
4427         version      = other.version;
4428         majorVersion = other.majorVersion;
4429         minorVersion = other.minorVersion;
4430         microVersion = other.microVersion;
4431         fileName     = other.fileName;
4432         attrs        = other.attrs;
4433         requireList  = other.requireList;
4434         }
4438     int get(int pos);
4440     int skipwhite(int pos);
4442     int getword(int pos, String &ret);
4444     void parseRequires();
4446     void parseVersion();
4448     bool parse(const String &buf);
4450     void dumpAttrs();
4452     String name;
4454     String description;
4456     String cflags;
4458     String libs;
4460     String requires;
4462     String version;
4464     int majorVersion;
4466     int minorVersion;
4468     int microVersion;
4470     String fileName;
4472     std::map<String, String> attrs;
4474     std::vector<String> requireList;
4476     char *parsebuf;
4477     int parselen;
4478 };
4481 /**
4482  * Get a character from the buffer at pos.  If out of range,
4483  * return -1 for safety
4484  */
4485 int PkgConfig::get(int pos)
4487     if (pos>parselen)
4488         return -1;
4489     return parsebuf[pos];
4494 /**
4495  *  Skip over all whitespace characters beginning at pos.  Return
4496  *  the position of the first non-whitespace character.
4497  */
4498 int PkgConfig::skipwhite(int pos)
4500     while (pos < parselen)
4501         {
4502         int ch = get(pos);
4503         if (ch < 0)
4504             break;
4505         if (!isspace(ch))
4506             break;
4507         pos++;
4508         }
4509     return pos;
4513 /**
4514  *  Parse the buffer beginning at pos, for a word.  Fill
4515  *  'ret' with the result.  Return the position after the
4516  *  word.
4517  */
4518 int PkgConfig::getword(int pos, String &ret)
4520     while (pos < parselen)
4521         {
4522         int ch = get(pos);
4523         if (ch < 0)
4524             break;
4525         if (!isalnum(ch) && ch != '_' && ch != '-'&& ch != '.')
4526             break;
4527         ret.push_back((char)ch);
4528         pos++;
4529         }
4530     return pos;
4533 void PkgConfig::parseRequires()
4535     if (requires.size() == 0)
4536         return;
4537     parsebuf = (char *)requires.c_str();
4538     parselen = requires.size();
4539     int pos = 0;
4540     while (pos < parselen)
4541         {
4542         pos = skipwhite(pos);
4543         String val;
4544         int pos2 = getword(pos, val);
4545         if (pos2 == pos)
4546             break;
4547         pos = pos2;
4548         //trace("val %s", val.c_str());
4549         requireList.push_back(val);
4550         }
4553 static int getint(const String str)
4555     char *s = (char *)str.c_str();
4556     char *ends = NULL;
4557     long val = strtol(s, &ends, 10);
4558     if (ends == s)
4559         return 0L;
4560     else
4561         return val;
4564 void PkgConfig::parseVersion()
4566     if (version.size() == 0)
4567         return;
4568     String s1, s2, s3;
4569     unsigned int pos = 0;
4570     unsigned int pos2 = version.find('.', pos);
4571     if (pos2 == version.npos)
4572         {
4573         s1 = version;
4574         }
4575     else
4576         {
4577         s1 = version.substr(pos, pos2-pos);
4578         pos = pos2;
4579         pos++;
4580         if (pos < version.size())
4581             {
4582             pos2 = version.find('.', pos);
4583             if (pos2 == version.npos)
4584                 {
4585                 s2 = version.substr(pos, version.size()-pos);
4586                 }
4587             else
4588                 {
4589                 s2 = version.substr(pos, pos2-pos);
4590                 pos = pos2;
4591                 pos++;
4592                 if (pos < version.size())
4593                     s3 = version.substr(pos, pos2-pos);
4594                 }
4595             }
4596         }
4598     majorVersion = getint(s1);
4599     minorVersion = getint(s2);
4600     microVersion = getint(s3);
4601     //trace("version:%d.%d.%d", majorVersion,
4602     //          minorVersion, microVersion );
4606 bool PkgConfig::parse(const String &buf)
4608     init();
4610     parsebuf = (char *)buf.c_str();
4611     parselen = buf.size();
4612     int pos = 0;
4615     while (pos < parselen)
4616         {
4617         String attrName;
4618         pos = skipwhite(pos);
4619         int ch = get(pos);
4620         if (ch == '#')
4621             {
4622             //comment.  eat the rest of the line
4623             while (pos < parselen)
4624                 {
4625                 ch = get(pos);
4626                 if (ch == '\n' || ch < 0)
4627                     break;
4628                 pos++;
4629                 }
4630             continue;
4631             }
4632         pos = getword(pos, attrName);
4633         if (attrName.size() == 0)
4634             continue;
4635         pos = skipwhite(pos);
4636         ch = get(pos);
4637         if (ch != ':' && ch != '=')
4638             {
4639             error("expected ':' or '='");
4640             return false;
4641             }
4642         pos++;
4643         pos = skipwhite(pos);
4644         String attrVal;
4645         while (pos < parselen)
4646             {
4647             ch = get(pos);
4648             if (ch == '\n' || ch < 0)
4649                 break;
4650             else if (ch == '$' && get(pos+1) == '{')
4651                 {
4652                 //#  this is a ${substitution}
4653                 pos += 2;
4654                 String subName;
4655                 while (pos < parselen)
4656                     {
4657                     ch = get(pos);
4658                     if (ch < 0)
4659                         {
4660                         error("unterminated substitution");
4661                         return false;
4662                         }
4663                     else if (ch == '}')
4664                         break;
4665                     else
4666                         subName.push_back((char)ch);
4667                     pos++;
4668                     }
4669                 //trace("subName:%s", subName.c_str());
4670                 String subVal = attrs[subName];
4671                 //trace("subVal:%s", subVal.c_str());
4672                 attrVal.append(subVal);
4673                 }
4674             else
4675                 attrVal.push_back((char)ch);
4676             pos++;
4677             }
4679         attrVal = trim(attrVal);
4680         attrs[attrName] = attrVal;
4682         if (attrName == "Name")
4683             name = attrVal;
4684         else if (attrName == "Description")
4685             description = attrVal;
4686         else if (attrName == "Cflags")
4687             cflags = attrVal;
4688         else if (attrName == "Libs")
4689             libs = attrVal;
4690         else if (attrName == "Requires")
4691             requires = attrVal;
4692         else if (attrName == "Version")
4693             version = attrVal;
4695         //trace("name:'%s'  value:'%s'",
4696         //      attrName.c_str(), attrVal.c_str());
4697         }
4700     parseRequires();
4701     parseVersion();
4703     return true;
4706 void PkgConfig::dumpAttrs()
4708     //trace("### PkgConfig attributes for %s", fileName.c_str());
4709     std::map<String, String>::iterator iter;
4710     for (iter=attrs.begin() ; iter!=attrs.end() ; iter++)
4711         {
4712         trace("   %s = %s", iter->first.c_str(), iter->second.c_str());
4713         }
4717 bool PkgConfig::readFile(const String &fileNameArg)
4719     fileName = fileNameArg;
4721     FILE *f = fopen(fileName.c_str(), "r");
4722     if (!f)
4723         {
4724         error("cannot open file '%s' for reading", fileName.c_str());
4725         return false;
4726         }
4727     String buf;
4728     while (true)
4729         {
4730         int ch = fgetc(f);
4731         if (ch < 0)
4732             break;
4733         buf.push_back((char)ch);
4734         }
4735     fclose(f);
4737     //trace("####### File:\n%s", buf.c_str());
4738     if (!parse(buf))
4739         {
4740         return false;
4741         }
4743     dumpAttrs();
4745     return true;
4752 //########################################################################
4753 //# D E P T O O L
4754 //########################################################################
4758 /**
4759  *  Class which holds information for each file.
4760  */
4761 class FileRec
4763 public:
4765     typedef enum
4766         {
4767         UNKNOWN,
4768         CFILE,
4769         HFILE,
4770         OFILE
4771         } FileType;
4773     /**
4774      *  Constructor
4775      */
4776     FileRec()
4777         { init(); type = UNKNOWN; }
4779     /**
4780      *  Copy constructor
4781      */
4782     FileRec(const FileRec &other)
4783         { init(); assign(other); }
4784     /**
4785      *  Constructor
4786      */
4787     FileRec(int typeVal)
4788         { init(); type = typeVal; }
4789     /**
4790      *  Assignment operator
4791      */
4792     FileRec &operator=(const FileRec &other)
4793         { init(); assign(other); return *this; }
4796     /**
4797      *  Destructor
4798      */
4799     ~FileRec()
4800         {}
4802     /**
4803      *  Directory part of the file name
4804      */
4805     String path;
4807     /**
4808      *  Base name, sans directory and suffix
4809      */
4810     String baseName;
4812     /**
4813      *  File extension, such as cpp or h
4814      */
4815     String suffix;
4817     /**
4818      *  Type of file: CFILE, HFILE, OFILE
4819      */
4820     int type;
4822     /**
4823      * Used to list files ref'd by this one
4824      */
4825     std::map<String, FileRec *> files;
4828 private:
4830     void init()
4831         {
4832         }
4834     void assign(const FileRec &other)
4835         {
4836         type     = other.type;
4837         baseName = other.baseName;
4838         suffix   = other.suffix;
4839         files    = other.files;
4840         }
4842 };
4846 /**
4847  *  Simpler dependency record
4848  */
4849 class DepRec
4851 public:
4853     /**
4854      *  Constructor
4855      */
4856     DepRec()
4857         {init();}
4859     /**
4860      *  Copy constructor
4861      */
4862     DepRec(const DepRec &other)
4863         {init(); assign(other);}
4864     /**
4865      *  Constructor
4866      */
4867     DepRec(const String &fname)
4868         {init(); name = fname; }
4869     /**
4870      *  Assignment operator
4871      */
4872     DepRec &operator=(const DepRec &other)
4873         {init(); assign(other); return *this;}
4876     /**
4877      *  Destructor
4878      */
4879     ~DepRec()
4880         {}
4882     /**
4883      *  Directory part of the file name
4884      */
4885     String path;
4887     /**
4888      *  Base name, without the path and suffix
4889      */
4890     String name;
4892     /**
4893      *  Suffix of the source
4894      */
4895     String suffix;
4898     /**
4899      * Used to list files ref'd by this one
4900      */
4901     std::vector<String> files;
4904 private:
4906     void init()
4907         {
4908         }
4910     void assign(const DepRec &other)
4911         {
4912         path     = other.path;
4913         name     = other.name;
4914         suffix   = other.suffix;
4915         files    = other.files; //avoid recursion
4916         }
4918 };
4921 class DepTool : public MakeBase
4923 public:
4925     /**
4926      *  Constructor
4927      */
4928     DepTool()
4929         { init(); }
4931     /**
4932      *  Copy constructor
4933      */
4934     DepTool(const DepTool &other)
4935         { init(); assign(other); }
4937     /**
4938      *  Assignment operator
4939      */
4940     DepTool &operator=(const DepTool &other)
4941         { init(); assign(other); return *this; }
4944     /**
4945      *  Destructor
4946      */
4947     ~DepTool()
4948         {}
4951     /**
4952      *  Reset this section of code
4953      */
4954     virtual void init();
4955     
4956     /**
4957      *  Reset this section of code
4958      */
4959     virtual void assign(const DepTool &other)
4960         {
4961         }
4962     
4963     /**
4964      *  Sets the source directory which will be scanned
4965      */
4966     virtual void setSourceDirectory(const String &val)
4967         { sourceDir = val; }
4969     /**
4970      *  Returns the source directory which will be scanned
4971      */
4972     virtual String getSourceDirectory()
4973         { return sourceDir; }
4975     /**
4976      *  Sets the list of files within the directory to analyze
4977      */
4978     virtual void setFileList(const std::vector<String> &list)
4979         { fileList = list; }
4981     /**
4982      * Creates the list of all file names which will be
4983      * candidates for further processing.  Reads make.exclude
4984      * to see which files for directories to leave out.
4985      */
4986     virtual bool createFileList();
4989     /**
4990      *  Generates the forward dependency list
4991      */
4992     virtual bool generateDependencies();
4995     /**
4996      *  Generates the forward dependency list, saving the file
4997      */
4998     virtual bool generateDependencies(const String &);
5001     /**
5002      *  Load a dependency file
5003      */
5004     std::vector<DepRec> loadDepFile(const String &fileName);
5006     /**
5007      *  Load a dependency file, generating one if necessary
5008      */
5009     std::vector<DepRec> getDepFile(const String &fileName,
5010               bool forceRefresh);
5012     /**
5013      *  Save a dependency file
5014      */
5015     bool saveDepFile(const String &fileName);
5018 private:
5021     /**
5022      *
5023      */
5024     void parseName(const String &fullname,
5025                    String &path,
5026                    String &basename,
5027                    String &suffix);
5029     /**
5030      *
5031      */
5032     int get(int pos);
5034     /**
5035      *
5036      */
5037     int skipwhite(int pos);
5039     /**
5040      *
5041      */
5042     int getword(int pos, String &ret);
5044     /**
5045      *
5046      */
5047     bool sequ(int pos, char *key);
5049     /**
5050      *
5051      */
5052     bool addIncludeFile(FileRec *frec, const String &fname);
5054     /**
5055      *
5056      */
5057     bool scanFile(const String &fname, FileRec *frec);
5059     /**
5060      *
5061      */
5062     bool processDependency(FileRec *ofile, FileRec *include);
5064     /**
5065      *
5066      */
5067     String sourceDir;
5069     /**
5070      *
5071      */
5072     std::vector<String> fileList;
5074     /**
5075      *
5076      */
5077     std::vector<String> directories;
5079     /**
5080      * A list of all files which will be processed for
5081      * dependencies.
5082      */
5083     std::map<String, FileRec *> allFiles;
5085     /**
5086      * The list of .o files, and the
5087      * dependencies upon them.
5088      */
5089     std::map<String, FileRec *> oFiles;
5091     int depFileSize;
5092     char *depFileBuf;
5094     static const int readBufSize = 8192;
5095     char readBuf[8193];//byte larger
5097 };
5103 /**
5104  *  Clean up after processing.  Called by the destructor, but should
5105  *  also be called before the object is reused.
5106  */
5107 void DepTool::init()
5109     sourceDir = ".";
5111     fileList.clear();
5112     directories.clear();
5113     
5114     //clear output file list
5115     std::map<String, FileRec *>::iterator iter;
5116     for (iter=oFiles.begin(); iter!=oFiles.end() ; iter++)
5117         delete iter->second;
5118     oFiles.clear();
5120     //allFiles actually contains the master copies. delete them
5121     for (iter= allFiles.begin(); iter!=allFiles.end() ; iter++)
5122         delete iter->second;
5123     allFiles.clear(); 
5130 /**
5131  *  Parse a full path name into path, base name, and suffix
5132  */
5133 void DepTool::parseName(const String &fullname,
5134                         String &path,
5135                         String &basename,
5136                         String &suffix)
5138     if (fullname.size() < 2)
5139         return;
5141     unsigned int pos = fullname.find_last_of('/');
5142     if (pos != fullname.npos && pos<fullname.size()-1)
5143         {
5144         path = fullname.substr(0, pos);
5145         pos++;
5146         basename = fullname.substr(pos, fullname.size()-pos);
5147         }
5148     else
5149         {
5150         path = "";
5151         basename = fullname;
5152         }
5154     pos = basename.find_last_of('.');
5155     if (pos != basename.npos && pos<basename.size()-1)
5156         {
5157         suffix   = basename.substr(pos+1, basename.size()-pos-1);
5158         basename = basename.substr(0, pos);
5159         }
5161     //trace("parsename:%s %s %s", path.c_str(),
5162     //        basename.c_str(), suffix.c_str()); 
5167 /**
5168  *  Generate our internal file list.
5169  */
5170 bool DepTool::createFileList()
5173     for (unsigned int i=0 ; i<fileList.size() ; i++)
5174         {
5175         String fileName = fileList[i];
5176         //trace("## FileName:%s", fileName.c_str());
5177         String path;
5178         String basename;
5179         String sfx;
5180         parseName(fileName, path, basename, sfx);
5181         if (sfx == "cpp" || sfx == "c" || sfx == "cxx"   ||
5182             sfx == "cc" || sfx == "CC")
5183             {
5184             FileRec *fe         = new FileRec(FileRec::CFILE);
5185             fe->path            = path;
5186             fe->baseName        = basename;
5187             fe->suffix          = sfx;
5188             allFiles[fileName]  = fe;
5189             }
5190         else if (sfx == "h"   ||  sfx == "hh"  ||
5191                  sfx == "hpp" ||  sfx == "hxx")
5192             {
5193             FileRec *fe         = new FileRec(FileRec::HFILE);
5194             fe->path            = path;
5195             fe->baseName        = basename;
5196             fe->suffix          = sfx;
5197             allFiles[fileName]  = fe;
5198             }
5199         }
5201     if (!listDirectories(sourceDir, "", directories))
5202         return false;
5203         
5204     return true;
5211 /**
5212  * Get a character from the buffer at pos.  If out of range,
5213  * return -1 for safety
5214  */
5215 int DepTool::get(int pos)
5217     if (pos>depFileSize)
5218         return -1;
5219     return depFileBuf[pos];
5224 /**
5225  *  Skip over all whitespace characters beginning at pos.  Return
5226  *  the position of the first non-whitespace character.
5227  */
5228 int DepTool::skipwhite(int pos)
5230     while (pos < depFileSize)
5231         {
5232         int ch = get(pos);
5233         if (ch < 0)
5234             break;
5235         if (!isspace(ch))
5236             break;
5237         pos++;
5238         }
5239     return pos;
5243 /**
5244  *  Parse the buffer beginning at pos, for a word.  Fill
5245  *  'ret' with the result.  Return the position after the
5246  *  word.
5247  */
5248 int DepTool::getword(int pos, String &ret)
5250     while (pos < depFileSize)
5251         {
5252         int ch = get(pos);
5253         if (ch < 0)
5254             break;
5255         if (isspace(ch))
5256             break;
5257         ret.push_back((char)ch);
5258         pos++;
5259         }
5260     return pos;
5263 /**
5264  * Return whether the sequence of characters in the buffer
5265  * beginning at pos match the key,  for the length of the key
5266  */
5267 bool DepTool::sequ(int pos, char *key)
5269     while (*key)
5270         {
5271         if (*key != get(pos))
5272             return false;
5273         key++; pos++;
5274         }
5275     return true;
5280 /**
5281  *  Add an include file name to a file record.  If the name
5282  *  is not found in allFiles explicitly, try prepending include
5283  *  directory names to it and try again.
5284  */
5285 bool DepTool::addIncludeFile(FileRec *frec, const String &iname)
5287     //# if the name is an exact match to a path name
5288     //# in allFiles, like "myinc.h"
5289     std::map<String, FileRec *>::iterator iter =
5290            allFiles.find(iname);
5291     if (iter != allFiles.end()) //already exists
5292         {
5293          //h file in same dir
5294         FileRec *other = iter->second;
5295         //trace("local: '%s'", iname.c_str());
5296         frec->files[iname] = other;
5297         return true;
5298         }
5299     else 
5300         {
5301         //## Ok, it was not found directly
5302         //look in other dirs
5303         std::vector<String>::iterator diter;
5304         for (diter=directories.begin() ;
5305              diter!=directories.end() ; diter++)
5306             {
5307             String dfname = *diter;
5308             dfname.append("/");
5309             dfname.append(iname);
5310             URI fullPathURI(dfname);  //normalize path name
5311             String fullPath = fullPathURI.getPath();
5312             if (fullPath[0] == '/')
5313                 fullPath = fullPath.substr(1);
5314             //trace("Normalized %s to %s", dfname.c_str(), fullPath.c_str());
5315             iter = allFiles.find(fullPath);
5316             if (iter != allFiles.end())
5317                 {
5318                 FileRec *other = iter->second;
5319                 //trace("other: '%s'", iname.c_str());
5320                 frec->files[fullPath] = other;
5321                 return true;
5322                 }
5323             }
5324         }
5325     return true;
5330 /**
5331  *  Lightly parse a file to find the #include directives.  Do
5332  *  a bit of state machine stuff to make sure that the directive
5333  *  is valid.  (Like not in a comment).
5334  */
5335 bool DepTool::scanFile(const String &fname, FileRec *frec)
5337     String fileName;
5338     if (sourceDir.size() > 0)
5339         {
5340         fileName.append(sourceDir);
5341         fileName.append("/");
5342         }
5343     fileName.append(fname);
5344     String nativeName = getNativePath(fileName);
5345     FILE *f = fopen(nativeName.c_str(), "r");
5346     if (!f)
5347         {
5348         error("Could not open '%s' for reading", fname.c_str());
5349         return false;
5350         }
5351     String buf;
5352     while (!feof(f))
5353         {
5354         int len = fread(readBuf, 1, readBufSize, f);
5355         readBuf[len] = '\0';
5356         buf.append(readBuf);
5357         }
5358     fclose(f);
5360     depFileSize = buf.size();
5361     depFileBuf  = (char *)buf.c_str();
5362     int pos = 0;
5365     while (pos < depFileSize)
5366         {
5367         //trace("p:%c", get(pos));
5369         //# Block comment
5370         if (get(pos) == '/' && get(pos+1) == '*')
5371             {
5372             pos += 2;
5373             while (pos < depFileSize)
5374                 {
5375                 if (get(pos) == '*' && get(pos+1) == '/')
5376                     {
5377                     pos += 2;
5378                     break;
5379                     }
5380                 else
5381                     pos++;
5382                 }
5383             }
5384         //# Line comment
5385         else if (get(pos) == '/' && get(pos+1) == '/')
5386             {
5387             pos += 2;
5388             while (pos < depFileSize)
5389                 {
5390                 if (get(pos) == '\n')
5391                     {
5392                     pos++;
5393                     break;
5394                     }
5395                 else
5396                     pos++;
5397                 }
5398             }
5399         //# #include! yaay
5400         else if (sequ(pos, "#include"))
5401             {
5402             pos += 8;
5403             pos = skipwhite(pos);
5404             String iname;
5405             pos = getword(pos, iname);
5406             if (iname.size()>2)
5407                 {
5408                 iname = iname.substr(1, iname.size()-2);
5409                 addIncludeFile(frec, iname);
5410                 }
5411             }
5412         else
5413             {
5414             pos++;
5415             }
5416         }
5418     return true;
5423 /**
5424  *  Recursively check include lists to find all files in allFiles to which
5425  *  a given file is dependent.
5426  */
5427 bool DepTool::processDependency(FileRec *ofile, FileRec *include)
5429     std::map<String, FileRec *>::iterator iter;
5430     for (iter=include->files.begin() ; iter!=include->files.end() ; iter++)
5431         {
5432         String fname  = iter->first;
5433         if (ofile->files.find(fname) != ofile->files.end())
5434             {
5435             //trace("file '%s' already seen", fname.c_str());
5436             continue;
5437             }
5438         FileRec *child  = iter->second;
5439         ofile->files[fname] = child;
5440       
5441         processDependency(ofile, child);
5442         }
5445     return true;
5452 /**
5453  *  Generate the file dependency list.
5454  */
5455 bool DepTool::generateDependencies()
5457     std::map<String, FileRec *>::iterator iter;
5458     //# First pass.  Scan for all includes
5459     for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5460         {
5461         FileRec *frec = iter->second;
5462         if (!scanFile(iter->first, frec))
5463             {
5464             //quit?
5465             }
5466         }
5468     //# Second pass.  Scan for all includes
5469     for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5470         {
5471         FileRec *include = iter->second;
5472         if (include->type == FileRec::CFILE)
5473             {
5474             String cFileName   = iter->first;
5475             FileRec *ofile     = new FileRec(FileRec::OFILE);
5476             ofile->path        = include->path;
5477             ofile->baseName    = include->baseName;
5478             ofile->suffix      = include->suffix;
5479             String fname       = include->path;
5480             if (fname.size()>0)
5481                 fname.append("/");
5482             fname.append(include->baseName);
5483             fname.append(".o");
5484             oFiles[fname]    = ofile;
5485             //add the .c file first?   no, don't
5486             //ofile->files[cFileName] = include;
5487             
5488             //trace("ofile:%s", fname.c_str());
5490             processDependency(ofile, include);
5491             }
5492         }
5494       
5495     return true;
5500 /**
5501  *  High-level call to generate deps and optionally save them
5502  */
5503 bool DepTool::generateDependencies(const String &fileName)
5505     if (!createFileList())
5506         return false;
5507     if (!generateDependencies())
5508         return false;
5509     if (!saveDepFile(fileName))
5510         return false;
5511     return true;
5515 /**
5516  *   This saves the dependency cache.
5517  */
5518 bool DepTool::saveDepFile(const String &fileName)
5520     time_t tim;
5521     time(&tim);
5523     FILE *f = fopen(fileName.c_str(), "w");
5524     if (!f)
5525         {
5526         trace("cannot open '%s' for writing", fileName.c_str());
5527         }
5528     fprintf(f, "<?xml version='1.0'?>\n");
5529     fprintf(f, "<!--\n");
5530     fprintf(f, "########################################################\n");
5531     fprintf(f, "## File: build.dep\n");
5532     fprintf(f, "## Generated by BuildTool at :%s", ctime(&tim));
5533     fprintf(f, "########################################################\n");
5534     fprintf(f, "-->\n");
5536     fprintf(f, "<dependencies source='%s'>\n\n", sourceDir.c_str());
5537     std::map<String, FileRec *>::iterator iter;
5538     for (iter=oFiles.begin() ; iter!=oFiles.end() ; iter++)
5539         {
5540         FileRec *frec = iter->second;
5541         if (frec->type == FileRec::OFILE)
5542             {
5543             fprintf(f, "<object path='%s' name='%s' suffix='%s'>\n",
5544                  frec->path.c_str(), frec->baseName.c_str(), frec->suffix.c_str());
5545             std::map<String, FileRec *>::iterator citer;
5546             for (citer=frec->files.begin() ; citer!=frec->files.end() ; citer++)
5547                 {
5548                 String cfname = citer->first;
5549                 fprintf(f, "    <dep name='%s'/>\n", cfname.c_str());
5550                 }
5551             fprintf(f, "</object>\n\n");
5552             }
5553         }
5555     fprintf(f, "</dependencies>\n");
5556     fprintf(f, "\n");
5557     fprintf(f, "<!--\n");
5558     fprintf(f, "########################################################\n");
5559     fprintf(f, "## E N D\n");
5560     fprintf(f, "########################################################\n");
5561     fprintf(f, "-->\n");
5563     fclose(f);
5565     return true;
5571 /**
5572  *   This loads the dependency cache.
5573  */
5574 std::vector<DepRec> DepTool::loadDepFile(const String &depFile)
5576     std::vector<DepRec> result;
5577     
5578     Parser parser;
5579     Element *root = parser.parseFile(depFile.c_str());
5580     if (!root)
5581         {
5582         //error("Could not open %s for reading", depFile.c_str());
5583         return result;
5584         }
5586     if (root->getChildren().size()==0 ||
5587         root->getChildren()[0]->getName()!="dependencies")
5588         {
5589         error("Main xml element should be <dependencies>");
5590         delete root;
5591         return result;
5592         }
5594     //########## Start parsing
5595     Element *depList = root->getChildren()[0];
5597     std::vector<Element *> objects = depList->getChildren();
5598     for (unsigned int i=0 ; i<objects.size() ; i++)
5599         {
5600         Element *objectElem = objects[i];
5601         String tagName = objectElem->getName();
5602         if (tagName == "object")
5603             {
5604             String objName   = objectElem->getAttribute("name");
5605              //trace("object:%s", objName.c_str());
5606             DepRec depObject(objName);
5607             depObject.path   = objectElem->getAttribute("path");
5608             depObject.suffix = objectElem->getAttribute("suffix");
5609             //########## DESCRIPTION
5610             std::vector<Element *> depElems = objectElem->getChildren();
5611             for (unsigned int i=0 ; i<depElems.size() ; i++)
5612                 {
5613                 Element *depElem = depElems[i];
5614                 tagName = depElem->getName();
5615                 if (tagName == "dep")
5616                     {
5617                     String depName = depElem->getAttribute("name");
5618                     //trace("    dep:%s", depName.c_str());
5619                     depObject.files.push_back(depName);
5620                     }
5621                 }
5622             //Insert into the result list, in a sorted manner
5623             bool inserted = false;
5624             std::vector<DepRec>::iterator iter;
5625             for (iter = result.begin() ; iter != result.end() ; iter++)
5626                 {
5627                 String vpath = iter->path;
5628                 vpath.append("/");
5629                 vpath.append(iter->name);
5630                 String opath = depObject.path;
5631                 opath.append("/");
5632                 opath.append(depObject.name);
5633                 if (vpath > opath)
5634                     {
5635                     inserted = true;
5636                     iter = result.insert(iter, depObject);
5637                     break;
5638                     }
5639                 }
5640             if (!inserted)
5641                 result.push_back(depObject);
5642             }
5643         }
5645     delete root;
5647     return result;
5651 /**
5652  *   This loads the dependency cache.
5653  */
5654 std::vector<DepRec> DepTool::getDepFile(const String &depFile,
5655                    bool forceRefresh)
5657     std::vector<DepRec> result;
5658     if (forceRefresh)
5659         {
5660         generateDependencies(depFile);
5661         result = loadDepFile(depFile);
5662         }
5663     else
5664         {
5665         //try once
5666         result = loadDepFile(depFile);
5667         if (result.size() == 0)
5668             {
5669             //fail? try again
5670             generateDependencies(depFile);
5671             result = loadDepFile(depFile);
5672             }
5673         }
5674     return result;
5680 //########################################################################
5681 //# T A S K
5682 //########################################################################
5683 //forward decl
5684 class Target;
5685 class Make;
5687 /**
5688  *
5689  */
5690 class Task : public MakeBase
5693 public:
5695     typedef enum
5696         {
5697         TASK_NONE,
5698         TASK_CC,
5699         TASK_COPY,
5700         TASK_DELETE,
5701         TASK_JAR,
5702         TASK_JAVAC,
5703         TASK_LINK,
5704         TASK_MAKEFILE,
5705         TASK_MKDIR,
5706         TASK_MSGFMT,
5707         TASK_RANLIB,
5708         TASK_RC,
5709         TASK_SHAREDLIB,
5710         TASK_STATICLIB,
5711         TASK_STRIP,
5712         TASK_TOUCH,
5713         TASK_TSTAMP
5714         } TaskType;
5715         
5717     /**
5718      *
5719      */
5720     Task(MakeBase &par) : parent(par)
5721         { init(); }
5723     /**
5724      *
5725      */
5726     Task(const Task &other) : parent(other.parent)
5727         { init(); assign(other); }
5729     /**
5730      *
5731      */
5732     Task &operator=(const Task &other)
5733         { assign(other); return *this; }
5735     /**
5736      *
5737      */
5738     virtual ~Task()
5739         { }
5742     /**
5743      *
5744      */
5745     virtual MakeBase &getParent()
5746         { return parent; }
5748      /**
5749      *
5750      */
5751     virtual int  getType()
5752         { return type; }
5754     /**
5755      *
5756      */
5757     virtual void setType(int val)
5758         { type = val; }
5760     /**
5761      *
5762      */
5763     virtual String getName()
5764         { return name; }
5766     /**
5767      *
5768      */
5769     virtual bool execute()
5770         { return true; }
5772     /**
5773      *
5774      */
5775     virtual bool parse(Element *elem)
5776         { return true; }
5778     /**
5779      *
5780      */
5781     Task *createTask(Element *elem, int lineNr);
5784 protected:
5786     void init()
5787         {
5788         type = TASK_NONE;
5789         name = "none";
5790         }
5792     void assign(const Task &other)
5793         {
5794         type = other.type;
5795         name = other.name;
5796         }
5797         
5798     String getAttribute(Element *elem, const String &attrName)
5799         {
5800         String str;
5801         return str;
5802         }
5804     MakeBase &parent;
5806     int type;
5808     String name;
5809 };
5813 /**
5814  * This task runs the C/C++ compiler.  The compiler is invoked
5815  * for all .c or .cpp files which are newer than their correcsponding
5816  * .o files.  
5817  */
5818 class TaskCC : public Task
5820 public:
5822     TaskCC(MakeBase &par) : Task(par)
5823         {
5824         type = TASK_CC; name = "cc";
5825         ccCommand   = "gcc";
5826         cxxCommand  = "g++";
5827         source      = ".";
5828         dest        = ".";
5829         flags       = "";
5830         defines     = "";
5831         includes    = "";
5832         fileSet.clear();
5833         }
5835     virtual ~TaskCC()
5836         {}
5838     virtual bool needsCompiling(const FileRec &depRec,
5839               const String &src, const String &dest)
5840         {
5841         return false;
5842         }
5844     virtual bool execute()
5845         {
5846         if (!listFiles(parent, fileSet))
5847             return false;
5848             
5849         FILE *f = NULL;
5850         f = fopen("compile.lst", "w");
5852         bool refreshCache = false;
5853         String fullName = parent.resolve("build.dep");
5854         if (isNewerThan(parent.getURI().getPath(), fullName))
5855             {
5856             status("          : regenerating C/C++ dependency cache");
5857             refreshCache = true;
5858             }
5860         DepTool depTool;
5861         depTool.setSourceDirectory(source);
5862         depTool.setFileList(fileSet.getFiles());
5863         std::vector<DepRec> deps =
5864              depTool.getDepFile("build.dep", refreshCache);
5865         
5866         String incs;
5867         incs.append("-I");
5868         incs.append(parent.resolve("."));
5869         incs.append(" ");
5870         if (includes.size()>0)
5871             {
5872             incs.append(includes);
5873             incs.append(" ");
5874             }
5875         std::set<String> paths;
5876         std::vector<DepRec>::iterator viter;
5877         for (viter=deps.begin() ; viter!=deps.end() ; viter++)
5878             {
5879             DepRec dep = *viter;
5880             if (dep.path.size()>0)
5881                 paths.insert(dep.path);
5882             }
5883         if (source.size()>0)
5884             {
5885             incs.append(" -I");
5886             incs.append(parent.resolve(source));
5887             incs.append(" ");
5888             }
5889         std::set<String>::iterator setIter;
5890         for (setIter=paths.begin() ; setIter!=paths.end() ; setIter++)
5891             {
5892             incs.append(" -I");
5893             String dname;
5894             if (source.size()>0)
5895                 {
5896                 dname.append(source);
5897                 dname.append("/");
5898                 }
5899             dname.append(*setIter);
5900             incs.append(parent.resolve(dname));
5901             }
5902         std::vector<String> cfiles;
5903         for (viter=deps.begin() ; viter!=deps.end() ; viter++)
5904             {
5905             DepRec dep = *viter;
5907             //## Select command
5908             String sfx = dep.suffix;
5909             String command = ccCommand;
5910             if (sfx == "cpp" || sfx == "c++" || sfx == "cc"
5911                  || sfx == "CC")
5912                 command = cxxCommand;
5913  
5914             //## Make paths
5915             String destPath = dest;
5916             String srcPath  = source;
5917             if (dep.path.size()>0)
5918                 {
5919                 destPath.append("/");
5920                 destPath.append(dep.path);
5921                 srcPath.append("/");
5922                 srcPath.append(dep.path);
5923                 }
5924             //## Make sure destination directory exists
5925             if (!createDirectory(destPath))
5926                 return false;
5927                 
5928             //## Check whether it needs to be done
5929             String destName;
5930             if (destPath.size()>0)
5931                 {
5932                 destName.append(destPath);
5933                 destName.append("/");
5934                 }
5935             destName.append(dep.name);
5936             destName.append(".o");
5937             String destFullName = parent.resolve(destName);
5938             String srcName;
5939             if (srcPath.size()>0)
5940                 {
5941                 srcName.append(srcPath);
5942                 srcName.append("/");
5943                 }
5944             srcName.append(dep.name);
5945             srcName.append(".");
5946             srcName.append(dep.suffix);
5947             String srcFullName = parent.resolve(srcName);
5948             bool compileMe = false;
5949             if (isNewerThan(srcFullName, destFullName))
5950                 {
5951                 status("          : compile of %s required by %s",
5952                         destFullName.c_str(), srcFullName.c_str());
5953                 compileMe = true;
5954                 }
5955             else
5956                 {
5957                 for (unsigned int i=0 ; i<dep.files.size() ; i++)
5958                     {
5959                     String depName;
5960                     if (srcPath.size()>0)
5961                         {
5962                         depName.append(srcPath);
5963                         depName.append("/");
5964                         }
5965                     depName.append(dep.files[i]);
5966                     String depFullName = parent.resolve(depName);
5967                     if (isNewerThan(depFullName, destFullName))
5968                         {
5969                         status("          : compile of %s required by %s",
5970                                 destFullName.c_str(), depFullName.c_str());
5971                         compileMe = true;
5972                         break;
5973                         }
5974                     }
5975                 }
5976             if (!compileMe)
5977                 {
5978                 continue;
5979                 }
5981             //## Assemble the command
5982             String cmd = command;
5983             cmd.append(" -c ");
5984             cmd.append(flags);
5985             cmd.append(" ");
5986             cmd.append(defines);
5987             cmd.append(" ");
5988             cmd.append(incs);
5989             cmd.append(" ");
5990             cmd.append(srcFullName);
5991             cmd.append(" -o ");
5992             cmd.append(destFullName);
5994             //## Execute the command
5996             String outString, errString;
5997             bool ret = executeCommand(cmd.c_str(), "", outString, errString);
5999             if (f)
6000                 {
6001                 fprintf(f, "########################### File : %s\n",
6002                              srcFullName.c_str());
6003                 fprintf(f, "#### COMMAND ###\n");
6004                 int col = 0;
6005                 for (int i = 0 ; i < cmd.size() ; i++)
6006                     {
6007                     char ch = cmd[i];
6008                     if (isspace(ch)  && col > 63)
6009                         {
6010                         fputc('\n', f);
6011                         col = 0;
6012                         }
6013                     else
6014                         {
6015                         fputc(ch, f);
6016                         col++;
6017                         }
6018                     if (col > 76)
6019                         {
6020                         fputc('\n', f);
6021                         col = 0;
6022                         }
6023                     }
6024                 fprintf(f, "\n");
6025                 fprintf(f, "#### STDOUT ###\n%s\n", outString.c_str());
6026                 fprintf(f, "#### STDERR ###\n%s\n\n", errString.c_str());
6027                 }
6028             if (!ret)
6029                 {
6030                 error("problem compiling: %s", errString.c_str());
6031                 return false;
6032                 }
6033                 
6034             }
6036         if (f)
6037             {
6038             fclose(f);
6039             }
6040         
6041         return true;
6042         }
6044     virtual bool parse(Element *elem)
6045         {
6046         String s;
6047         if (!parent.getAttribute(elem, "command", s))
6048             return false;
6049         if (s.size()>0) { ccCommand = s; cxxCommand = s; }
6050         if (!parent.getAttribute(elem, "cc", s))
6051             return false;
6052         if (s.size()>0) ccCommand = s;
6053         if (!parent.getAttribute(elem, "cxx", s))
6054             return false;
6055         if (s.size()>0) cxxCommand = s;
6056         if (!parent.getAttribute(elem, "destdir", s))
6057             return false;
6058         if (s.size()>0) dest = s;
6060         std::vector<Element *> children = elem->getChildren();
6061         for (unsigned int i=0 ; i<children.size() ; i++)
6062             {
6063             Element *child = children[i];
6064             String tagName = child->getName();
6065             if (tagName == "flags")
6066                 {
6067                 if (!parent.getValue(child, flags))
6068                     return false;
6069                 flags = strip(flags);
6070                 }
6071             else if (tagName == "includes")
6072                 {
6073                 if (!parent.getValue(child, includes))
6074                     return false;
6075                 includes = strip(includes);
6076                 }
6077             else if (tagName == "defines")
6078                 {
6079                 if (!parent.getValue(child, defines))
6080                     return false;
6081                 defines = strip(defines);
6082                 }
6083             else if (tagName == "fileset")
6084                 {
6085                 if (!parseFileSet(child, parent, fileSet))
6086                     return false;
6087                 source = fileSet.getDirectory();
6088                 }
6089             }
6091         return true;
6092         }
6093         
6094 protected:
6096     String ccCommand;
6097     String cxxCommand;
6098     String source;
6099     String dest;
6100     String flags;
6101     String defines;
6102     String includes;
6103     FileSet fileSet;
6104     
6105 };
6109 /**
6110  *
6111  */
6112 class TaskCopy : public Task
6114 public:
6116     typedef enum
6117         {
6118         CP_NONE,
6119         CP_TOFILE,
6120         CP_TODIR
6121         } CopyType;
6123     TaskCopy(MakeBase &par) : Task(par)
6124         {
6125         type = TASK_COPY; name = "copy";
6126         cptype = CP_NONE;
6127         verbose = false;
6128         haveFileSet = false;
6129         }
6131     virtual ~TaskCopy()
6132         {}
6134     virtual bool execute()
6135         {
6136         switch (cptype)
6137            {
6138            case CP_TOFILE:
6139                {
6140                if (fileName.size()>0)
6141                    {
6142                    status("          : %s to %s",
6143                         fileName.c_str(), toFileName.c_str());
6144                    String fullSource = parent.resolve(fileName);
6145                    String fullDest = parent.resolve(toFileName);
6146                    //trace("copy %s to file %s", fullSource.c_str(),
6147                    //                       fullDest.c_str());
6148                    if (!isRegularFile(fullSource))
6149                        {
6150                        error("copy : file %s does not exist", fullSource.c_str());
6151                        return false;
6152                        }
6153                    if (!isNewerThan(fullSource, fullDest))
6154                        {
6155                        return true;
6156                        }
6157                    if (!copyFile(fullSource, fullDest))
6158                        return false;
6159                    status("          : 1 file copied");
6160                    }
6161                return true;
6162                }
6163            case CP_TODIR:
6164                {
6165                if (haveFileSet)
6166                    {
6167                    if (!listFiles(parent, fileSet))
6168                        return false;
6169                    String fileSetDir = fileSet.getDirectory();
6171                    status("          : %s to %s",
6172                        fileSetDir.c_str(), toDirName.c_str());
6174                    int nrFiles = 0;
6175                    for (unsigned int i=0 ; i<fileSet.size() ; i++)
6176                        {
6177                        String fileName = fileSet[i];
6179                        String sourcePath;
6180                        if (fileSetDir.size()>0)
6181                            {
6182                            sourcePath.append(fileSetDir);
6183                            sourcePath.append("/");
6184                            }
6185                        sourcePath.append(fileName);
6186                        String fullSource = parent.resolve(sourcePath);
6187                        
6188                        //Get the immediate parent directory's base name
6189                        String baseFileSetDir = fileSetDir;
6190                        unsigned int pos = baseFileSetDir.find_last_of('/');
6191                        if (pos!=baseFileSetDir.npos &&
6192                                   pos < baseFileSetDir.size()-1)
6193                            baseFileSetDir =
6194                               baseFileSetDir.substr(pos+1,
6195                                    baseFileSetDir.size());
6196                        //Now make the new path
6197                        String destPath;
6198                        if (toDirName.size()>0)
6199                            {
6200                            destPath.append(toDirName);
6201                            destPath.append("/");
6202                            }
6203                        if (baseFileSetDir.size()>0)
6204                            {
6205                            destPath.append(baseFileSetDir);
6206                            destPath.append("/");
6207                            }
6208                        destPath.append(fileName);
6209                        String fullDest = parent.resolve(destPath);
6210                        //trace("fileName:%s", fileName.c_str());
6211                        //trace("copy %s to new dir : %s", fullSource.c_str(),
6212                        //                   fullDest.c_str());
6213                        if (!isNewerThan(fullSource, fullDest))
6214                            {
6215                            //trace("copy skipping %s", fullSource.c_str());
6216                            continue;
6217                            }
6218                        if (!copyFile(fullSource, fullDest))
6219                            return false;
6220                        nrFiles++;
6221                        }
6222                    status("          : %d file(s) copied", nrFiles);
6223                    }
6224                else //file source
6225                    {
6226                    //For file->dir we want only the basename of
6227                    //the source appended to the dest dir
6228                    status("          : %s to %s", 
6229                        fileName.c_str(), toDirName.c_str());
6230                    String baseName = fileName;
6231                    unsigned int pos = baseName.find_last_of('/');
6232                    if (pos!=baseName.npos && pos<baseName.size()-1)
6233                        baseName = baseName.substr(pos+1, baseName.size());
6234                    String fullSource = parent.resolve(fileName);
6235                    String destPath;
6236                    if (toDirName.size()>0)
6237                        {
6238                        destPath.append(toDirName);
6239                        destPath.append("/");
6240                        }
6241                    destPath.append(baseName);
6242                    String fullDest = parent.resolve(destPath);
6243                    //trace("copy %s to new dir : %s", fullSource.c_str(),
6244                    //                       fullDest.c_str());
6245                    if (!isRegularFile(fullSource))
6246                        {
6247                        error("copy : file %s does not exist", fullSource.c_str());
6248                        return false;
6249                        }
6250                    if (!isNewerThan(fullSource, fullDest))
6251                        {
6252                        return true;
6253                        }
6254                    if (!copyFile(fullSource, fullDest))
6255                        return false;
6256                    status("          : 1 file copied");
6257                    }
6258                return true;
6259                }
6260            }
6261         return true;
6262         }
6265     virtual bool parse(Element *elem)
6266         {
6267         if (!parent.getAttribute(elem, "file", fileName))
6268             return false;
6269         if (!parent.getAttribute(elem, "tofile", toFileName))
6270             return false;
6271         if (toFileName.size() > 0)
6272             cptype = CP_TOFILE;
6273         if (!parent.getAttribute(elem, "todir", toDirName))
6274             return false;
6275         if (toDirName.size() > 0)
6276             cptype = CP_TODIR;
6277         String ret;
6278         if (!parent.getAttribute(elem, "verbose", ret))
6279             return false;
6280         if (ret.size()>0 && !getBool(ret, verbose))
6281             return false;
6282             
6283         haveFileSet = false;
6284         
6285         std::vector<Element *> children = elem->getChildren();
6286         for (unsigned int i=0 ; i<children.size() ; i++)
6287             {
6288             Element *child = children[i];
6289             String tagName = child->getName();
6290             if (tagName == "fileset")
6291                 {
6292                 if (!parseFileSet(child, parent, fileSet))
6293                     {
6294                     error("problem getting fileset");
6295                     return false;
6296                     }
6297                 haveFileSet = true;
6298                 }
6299             }
6301         //Perform validity checks
6302         if (fileName.size()>0 && fileSet.size()>0)
6303             {
6304             error("<copy> can only have one of : file= and <fileset>");
6305             return false;
6306             }
6307         if (toFileName.size()>0 && toDirName.size()>0)
6308             {
6309             error("<copy> can only have one of : tofile= or todir=");
6310             return false;
6311             }
6312         if (haveFileSet && toDirName.size()==0)
6313             {
6314             error("a <copy> task with a <fileset> must have : todir=");
6315             return false;
6316             }
6317         if (cptype == CP_TOFILE && fileName.size()==0)
6318             {
6319             error("<copy> tofile= must be associated with : file=");
6320             return false;
6321             }
6322         if (cptype == CP_TODIR && fileName.size()==0 && !haveFileSet)
6323             {
6324             error("<copy> todir= must be associated with : file= or <fileset>");
6325             return false;
6326             }
6328         return true;
6329         }
6330         
6331 private:
6333     int cptype;
6334     String fileName;
6335     FileSet fileSet;
6336     String toFileName;
6337     String toDirName;
6338     bool verbose;
6339     bool haveFileSet;
6340 };
6343 /**
6344  *
6345  */
6346 class TaskDelete : public Task
6348 public:
6350     typedef enum
6351         {
6352         DEL_FILE,
6353         DEL_DIR,
6354         DEL_FILESET
6355         } DeleteType;
6357     TaskDelete(MakeBase &par) : Task(par)
6358         { 
6359           type        = TASK_DELETE;
6360           name        = "delete";
6361           delType     = DEL_FILE;
6362           verbose     = false;
6363           quiet       = false;
6364           failOnError = true;
6365         }
6367     virtual ~TaskDelete()
6368         {}
6370     virtual bool execute()
6371         {
6372         struct stat finfo;
6373         switch (delType)
6374             {
6375             case DEL_FILE:
6376                 {
6377                 status("          : %s", fileName.c_str());
6378                 String fullName = parent.resolve(fileName);
6379                 char *fname = (char *)fullName.c_str();
6380                 //does not exist
6381                 if (stat(fname, &finfo)<0)
6382                     return true;
6383                 //exists but is not a regular file
6384                 if (!S_ISREG(finfo.st_mode))
6385                     {
6386                     error("<delete> failed. '%s' exists and is not a regular file",
6387                           fname);
6388                     return false;
6389                     }
6390                 if (remove(fname)<0)
6391                     {
6392                     error("<delete> failed: %s", strerror(errno));
6393                     return false;
6394                     }
6395                 return true;
6396                 }
6397             case DEL_DIR:
6398                 {
6399                 status("          : %s", dirName.c_str());
6400                 String fullDir = parent.resolve(dirName);
6401                 if (!removeDirectory(fullDir))
6402                     return false;
6403                 return true;
6404                 }
6405             }
6406         return true;
6407         }
6409     virtual bool parse(Element *elem)
6410         {
6411         if (!parent.getAttribute(elem, "file", fileName))
6412             return false;
6413         if (fileName.size() > 0)
6414             delType = DEL_FILE;
6415         if (!parent.getAttribute(elem, "dir", dirName))
6416             return false;
6417         if (dirName.size() > 0)
6418             delType = DEL_DIR;
6419         if (fileName.size()>0 && dirName.size()>0)
6420             {
6421             error("<delete> can have one attribute of file= or dir=");
6422             return false;
6423             }
6424         if (fileName.size()==0 && dirName.size()==0)
6425             {
6426             error("<delete> must have one attribute of file= or dir=");
6427             return false;
6428             }
6429         String ret;
6430         if (!parent.getAttribute(elem, "verbose", ret))
6431             return false;
6432         if (ret.size()>0 && !getBool(ret, verbose))
6433             return false;
6434         if (!parent.getAttribute(elem, "quiet", ret))
6435             return false;
6436         if (ret.size()>0 && !getBool(ret, quiet))
6437             return false;
6438         if (!parent.getAttribute(elem, "failonerror", ret))
6439             return false;
6440         if (ret.size()>0 && !getBool(ret, failOnError))
6441             return false;
6442         return true;
6443         }
6445 private:
6447     int delType;
6448     String dirName;
6449     String fileName;
6450     bool verbose;
6451     bool quiet;
6452     bool failOnError;
6453 };
6456 /**
6457  *
6458  */
6459 class TaskJar : public Task
6461 public:
6463     TaskJar(MakeBase &par) : Task(par)
6464         { type = TASK_JAR; name = "jar"; }
6466     virtual ~TaskJar()
6467         {}
6469     virtual bool execute()
6470         {
6471         return true;
6472         }
6474     virtual bool parse(Element *elem)
6475         {
6476         return true;
6477         }
6478 };
6481 /**
6482  *
6483  */
6484 class TaskJavac : public Task
6486 public:
6488     TaskJavac(MakeBase &par) : Task(par)
6489         { type = TASK_JAVAC; name = "javac"; }
6491     virtual ~TaskJavac()
6492         {}
6494     virtual bool execute()
6495         {
6496         return true;
6497         }
6499     virtual bool parse(Element *elem)
6500         {
6501         return true;
6502         }
6503 };
6506 /**
6507  *
6508  */
6509 class TaskLink : public Task
6511 public:
6513     TaskLink(MakeBase &par) : Task(par)
6514         {
6515         type = TASK_LINK; name = "link";
6516         command = "g++";
6517         doStrip = false;
6518                 stripCommand = "strip";
6519                 objcopyCommand = "objcopy";
6520         }
6522     virtual ~TaskLink()
6523         {}
6525     virtual bool execute()
6526         {
6527         if (!listFiles(parent, fileSet))
6528             return false;
6529         String fileSetDir = fileSet.getDirectory();
6530         //trace("%d files in %s", fileSet.size(), fileSetDir.c_str());
6531         bool doit = false;
6532         String fullTarget = parent.resolve(fileName);
6533         String cmd = command;
6534         cmd.append(" -o ");
6535         cmd.append(fullTarget);
6536         cmd.append(" ");
6537         cmd.append(flags);
6538         for (unsigned int i=0 ; i<fileSet.size() ; i++)
6539             {
6540             cmd.append(" ");
6541             String obj;
6542             if (fileSetDir.size()>0)
6543                 {
6544                 obj.append(fileSetDir);
6545                 obj.append("/");
6546                 }
6547             obj.append(fileSet[i]);
6548             String fullObj = parent.resolve(obj);
6549             cmd.append(fullObj);
6550             //trace("link: tgt:%s obj:%s", fullTarget.c_str(),
6551             //          fullObj.c_str());
6552             if (isNewerThan(fullObj, fullTarget))
6553                 doit = true;
6554             }
6555         cmd.append(" ");
6556         cmd.append(libs);
6557         if (!doit)
6558             {
6559             //trace("link not needed");
6560             return true;
6561             }
6562         //trace("LINK cmd:%s", cmd.c_str());
6565         String outbuf, errbuf;
6566         if (!executeCommand(cmd.c_str(), "", outbuf, errbuf))
6567             {
6568             error("LINK problem: %s", errbuf.c_str());
6569             return false;
6570             }
6572         if (symFileName.size()>0)
6573             {
6574             String symFullName = parent.resolve(symFileName);
6575             cmd = objcopyCommand;
6576             cmd.append(" --only-keep-debug ");
6577             cmd.append(getNativePath(fullTarget));
6578             cmd.append(" ");
6579             cmd.append(getNativePath(symFullName));
6580             if (!executeCommand(cmd, "", outbuf, errbuf))
6581                 {
6582                 error("<strip> symbol file failed : %s", errbuf.c_str());
6583                 return false;
6584                 }
6585             }
6586             
6587         if (doStrip)
6588             {
6589             cmd = stripCommand;
6590             cmd.append(" ");
6591             cmd.append(getNativePath(fullTarget));
6592             if (!executeCommand(cmd, "", outbuf, errbuf))
6593                {
6594                error("<strip> failed : %s", errbuf.c_str());
6595                return false;
6596                }
6597             }
6599         return true;
6600         }
6602     virtual bool parse(Element *elem)
6603         {
6604         String s;
6605         if (!parent.getAttribute(elem, "command", s))
6606             return false;
6607         if (s.size()>0)
6608             command = s;
6609         if (!parent.getAttribute(elem, "objcopycommand", s))
6610             return false;
6611         if (s.size()>0)
6612             objcopyCommand = s;
6613         if (!parent.getAttribute(elem, "stripcommand", s))
6614             return false;
6615         if (s.size()>0)
6616             stripCommand = s;
6617         if (!parent.getAttribute(elem, "out", fileName))
6618             return false;
6619         if (!parent.getAttribute(elem, "strip", s))
6620             return false;
6621         if (s.size()>0 && !getBool(s, doStrip))
6622             return false;
6623         if (!parent.getAttribute(elem, "symfile", symFileName))
6624             return false;
6625             
6626         std::vector<Element *> children = elem->getChildren();
6627         for (unsigned int i=0 ; i<children.size() ; i++)
6628             {
6629             Element *child = children[i];
6630             String tagName = child->getName();
6631             if (tagName == "fileset")
6632                 {
6633                 if (!parseFileSet(child, parent, fileSet))
6634                     return false;
6635                 }
6636             else if (tagName == "flags")
6637                 {
6638                 if (!parent.getValue(child, flags))
6639                     return false;
6640                 flags = strip(flags);
6641                 }
6642             else if (tagName == "libs")
6643                 {
6644                 if (!parent.getValue(child, libs))
6645                     return false;
6646                 libs = strip(libs);
6647                 }
6648             }
6649         return true;
6650         }
6652 private:
6654     String  command;
6655     String  fileName;
6656     String  flags;
6657     String  libs;
6658     FileSet fileSet;
6659     bool    doStrip;
6660     String  symFileName;
6661     String  stripCommand;
6662     String  objcopyCommand;
6664 };
6668 /**
6669  * Create a named directory
6670  */
6671 class TaskMakeFile : public Task
6673 public:
6675     TaskMakeFile(MakeBase &par) : Task(par)
6676         { type = TASK_MAKEFILE; name = "makefile"; }
6678     virtual ~TaskMakeFile()
6679         {}
6681     virtual bool execute()
6682         {
6683         status("          : %s", fileName.c_str());
6684         String fullName = parent.resolve(fileName);
6685         if (!isNewerThan(parent.getURI().getPath(), fullName))
6686             {
6687             //trace("skipped <makefile>");
6688             return true;
6689             }
6690         //trace("fullName:%s", fullName.c_str());
6691         FILE *f = fopen(fullName.c_str(), "w");
6692         if (!f)
6693             {
6694             error("<makefile> could not open %s for writing : %s",
6695                 fullName.c_str(), strerror(errno));
6696             return false;
6697             }
6698         for (unsigned int i=0 ; i<text.size() ; i++)
6699             fputc(text[i], f);
6700         fputc('\n', f);
6701         fclose(f);
6702         return true;
6703         }
6705     virtual bool parse(Element *elem)
6706         {
6707         if (!parent.getAttribute(elem, "file", fileName))
6708             return false;
6709         if (fileName.size() == 0)
6710             {
6711             error("<makefile> requires 'file=\"filename\"' attribute");
6712             return false;
6713             }
6714         if (!parent.getValue(elem, text))
6715             return false;
6716         text = leftJustify(text);
6717         //trace("dirname:%s", dirName.c_str());
6718         return true;
6719         }
6721 private:
6723     String fileName;
6724     String text;
6725 };
6729 /**
6730  * Create a named directory
6731  */
6732 class TaskMkDir : public Task
6734 public:
6736     TaskMkDir(MakeBase &par) : Task(par)
6737         { type = TASK_MKDIR; name = "mkdir"; }
6739     virtual ~TaskMkDir()
6740         {}
6742     virtual bool execute()
6743         {
6744         status("          : %s", dirName.c_str());
6745         String fullDir = parent.resolve(dirName);
6746         //trace("fullDir:%s", fullDir.c_str());
6747         if (!createDirectory(fullDir))
6748             return false;
6749         return true;
6750         }
6752     virtual bool parse(Element *elem)
6753         {
6754         if (!parent.getAttribute(elem, "dir", dirName))
6755             return false;
6756         if (dirName.size() == 0)
6757             {
6758             error("<mkdir> requires 'dir=\"dirname\"' attribute");
6759             return false;
6760             }
6761         return true;
6762         }
6764 private:
6766     String dirName;
6767 };
6771 /**
6772  * Create a named directory
6773  */
6774 class TaskMsgFmt: public Task
6776 public:
6778     TaskMsgFmt(MakeBase &par) : Task(par)
6779          {
6780          type    = TASK_MSGFMT;
6781          name    = "msgfmt";
6782          command = "msgfmt";
6783          owndir  = false;
6784          outName = "";
6785          }
6787     virtual ~TaskMsgFmt()
6788         {}
6790     virtual bool execute()
6791         {
6792         if (!listFiles(parent, fileSet))
6793             return false;
6794         String fileSetDir = fileSet.getDirectory();
6796         //trace("msgfmt: %d", fileSet.size());
6797         for (unsigned int i=0 ; i<fileSet.size() ; i++)
6798             {
6799             String fileName = fileSet[i];
6800             if (getSuffix(fileName) != "po")
6801                 continue;
6802             String sourcePath;
6803             if (fileSetDir.size()>0)
6804                 {
6805                 sourcePath.append(fileSetDir);
6806                 sourcePath.append("/");
6807                 }
6808             sourcePath.append(fileName);
6809             String fullSource = parent.resolve(sourcePath);
6811             String destPath;
6812             if (toDirName.size()>0)
6813                 {
6814                 destPath.append(toDirName);
6815                 destPath.append("/");
6816                 }
6817             if (owndir)
6818                 {
6819                 String subdir = fileName;
6820                 unsigned int pos = subdir.find_last_of('.');
6821                 if (pos != subdir.npos)
6822                     subdir = subdir.substr(0, pos);
6823                 destPath.append(subdir);
6824                 destPath.append("/");
6825                 }
6826             //Pick the output file name
6827             if (outName.size() > 0)
6828                 {
6829                 destPath.append(outName);
6830                 }
6831             else
6832                 {
6833                 destPath.append(fileName);
6834                 destPath[destPath.size()-2] = 'm';
6835                 }
6837             String fullDest = parent.resolve(destPath);
6839             if (!isNewerThan(fullSource, fullDest))
6840                 {
6841                 //trace("skip %s", fullSource.c_str());
6842                 continue;
6843                 }
6844                 
6845             String cmd = command;
6846             cmd.append(" ");
6847             cmd.append(fullSource);
6848             cmd.append(" -o ");
6849             cmd.append(fullDest);
6850             
6851             int pos = fullDest.find_last_of('/');
6852             if (pos>0)
6853                 {
6854                 String fullDestPath = fullDest.substr(0, pos);
6855                 if (!createDirectory(fullDestPath))
6856                     return false;
6857                 }
6861             String outString, errString;
6862             if (!executeCommand(cmd.c_str(), "", outString, errString))
6863                 {
6864                 error("<msgfmt> problem: %s", errString.c_str());
6865                 return false;
6866                 }
6867             }
6869         return true;
6870         }
6872     virtual bool parse(Element *elem)
6873         {
6874         String s;
6875         if (!parent.getAttribute(elem, "command", s))
6876             return false;
6877         if (s.size()>0)
6878             command = s;
6879         if (!parent.getAttribute(elem, "todir", toDirName))
6880             return false;
6881         if (!parent.getAttribute(elem, "out", outName))
6882             return false;
6883         if (!parent.getAttribute(elem, "owndir", s))
6884             return false;
6885         if (s.size()>0 && !getBool(s, owndir))
6886             return false;
6887             
6888         std::vector<Element *> children = elem->getChildren();
6889         for (unsigned int i=0 ; i<children.size() ; i++)
6890             {
6891             Element *child = children[i];
6892             String tagName = child->getName();
6893             if (tagName == "fileset")
6894                 {
6895                 if (!parseFileSet(child, parent, fileSet))
6896                     return false;
6897                 }
6898             }
6899         return true;
6900         }
6902 private:
6904     String  command;
6905     String  toDirName;
6906     String  outName;
6907     FileSet fileSet;
6908     bool    owndir;
6910 };
6916 /**
6917  *  Process an archive to allow random access
6918  */
6919 class TaskRanlib : public Task
6921 public:
6923     TaskRanlib(MakeBase &par) : Task(par)
6924         {
6925         type = TASK_RANLIB; name = "ranlib";
6926         command = "ranlib";
6927         }
6929     virtual ~TaskRanlib()
6930         {}
6932     virtual bool execute()
6933         {
6934         String fullName = parent.resolve(fileName);
6935         //trace("fullDir:%s", fullDir.c_str());
6936         String cmd = command;
6937         cmd.append(" ");
6938         cmd.append(fullName);
6939         String outbuf, errbuf;
6940         if (!executeCommand(cmd, "", outbuf, errbuf))
6941             return false;
6942         return true;
6943         }
6945     virtual bool parse(Element *elem)
6946         {
6947         String s;
6948         if (!parent.getAttribute(elem, "command", s))
6949             return false;
6950         if (s.size()>0)
6951            command = s;
6952         if (!parent.getAttribute(elem, "file", fileName))
6953             return false;
6954         if (fileName.size() == 0)
6955             {
6956             error("<ranlib> requires 'file=\"fileNname\"' attribute");
6957             return false;
6958             }
6959         return true;
6960         }
6962 private:
6964     String fileName;
6965     String command;
6966 };
6970 /**
6971  * Run the "ar" command to archive .o's into a .a
6972  */
6973 class TaskRC : public Task
6975 public:
6977     TaskRC(MakeBase &par) : Task(par)
6978         {
6979         type = TASK_RC; name = "rc";
6980         command = "windres";
6981         }
6983     virtual ~TaskRC()
6984         {}
6986     virtual bool execute()
6987         {
6988         String fullFile = parent.resolve(fileName);
6989         String fullOut  = parent.resolve(outName);
6990         if (!isNewerThan(fullFile, fullOut))
6991             return true;
6992         String cmd = command;
6993         cmd.append(" -o ");
6994         cmd.append(fullOut);
6995         cmd.append(" ");
6996         cmd.append(flags);
6997         cmd.append(" ");
6998         cmd.append(fullFile);
7000         String outString, errString;
7001         if (!executeCommand(cmd.c_str(), "", outString, errString))
7002             {
7003             error("RC problem: %s", errString.c_str());
7004             return false;
7005             }
7006         return true;
7007         }
7009     virtual bool parse(Element *elem)
7010         {
7011         if (!parent.getAttribute(elem, "command", command))
7012             return false;
7013         if (!parent.getAttribute(elem, "file", fileName))
7014             return false;
7015         if (!parent.getAttribute(elem, "out", outName))
7016             return false;
7017         std::vector<Element *> children = elem->getChildren();
7018         for (unsigned int i=0 ; i<children.size() ; i++)
7019             {
7020             Element *child = children[i];
7021             String tagName = child->getName();
7022             if (tagName == "flags")
7023                 {
7024                 if (!parent.getValue(child, flags))
7025                     return false;
7026                 }
7027             }
7028         return true;
7029         }
7031 private:
7033     String command;
7034     String flags;
7035     String fileName;
7036     String outName;
7038 };
7042 /**
7043  *  Collect .o's into a .so or DLL
7044  */
7045 class TaskSharedLib : public Task
7047 public:
7049     TaskSharedLib(MakeBase &par) : Task(par)
7050         {
7051         type = TASK_SHAREDLIB; name = "dll";
7052         command = "dllwrap";
7053         }
7055     virtual ~TaskSharedLib()
7056         {}
7058     virtual bool execute()
7059         {
7060         //trace("###########HERE %d", fileSet.size());
7061         bool doit = false;
7062         
7063         String fullOut = parent.resolve(fileName);
7064         //trace("ar fullout: %s", fullOut.c_str());
7065         
7066         if (!listFiles(parent, fileSet))
7067             return false;
7068         String fileSetDir = fileSet.getDirectory();
7070         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7071             {
7072             String fname;
7073             if (fileSetDir.size()>0)
7074                 {
7075                 fname.append(fileSetDir);
7076                 fname.append("/");
7077                 }
7078             fname.append(fileSet[i]);
7079             String fullName = parent.resolve(fname);
7080             //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
7081             if (isNewerThan(fullName, fullOut))
7082                 doit = true;
7083             }
7084         //trace("Needs it:%d", doit);
7085         if (!doit)
7086             {
7087             return true;
7088             }
7090         String cmd = "dllwrap";
7091         cmd.append(" -o ");
7092         cmd.append(fullOut);
7093         if (defFileName.size()>0)
7094             {
7095             cmd.append(" --def ");
7096             cmd.append(defFileName);
7097             cmd.append(" ");
7098             }
7099         if (impFileName.size()>0)
7100             {
7101             cmd.append(" --implib ");
7102             cmd.append(impFileName);
7103             cmd.append(" ");
7104             }
7105         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7106             {
7107             String fname;
7108             if (fileSetDir.size()>0)
7109                 {
7110                 fname.append(fileSetDir);
7111                 fname.append("/");
7112                 }
7113             fname.append(fileSet[i]);
7114             String fullName = parent.resolve(fname);
7116             cmd.append(" ");
7117             cmd.append(fullName);
7118             }
7119         cmd.append(" ");
7120         cmd.append(libs);
7122         String outString, errString;
7123         if (!executeCommand(cmd.c_str(), "", outString, errString))
7124             {
7125             error("<sharedlib> problem: %s", errString.c_str());
7126             return false;
7127             }
7129         return true;
7130         }
7132     virtual bool parse(Element *elem)
7133         {
7134         if (!parent.getAttribute(elem, "file", fileName))
7135             return false;
7136         if (!parent.getAttribute(elem, "import", impFileName))
7137             return false;
7138         if (!parent.getAttribute(elem, "def", defFileName))
7139             return false;
7140             
7141         std::vector<Element *> children = elem->getChildren();
7142         for (unsigned int i=0 ; i<children.size() ; i++)
7143             {
7144             Element *child = children[i];
7145             String tagName = child->getName();
7146             if (tagName == "fileset")
7147                 {
7148                 if (!parseFileSet(child, parent, fileSet))
7149                     return false;
7150                 }
7151             else if (tagName == "libs")
7152                 {
7153                 if (!parent.getValue(child, libs))
7154                     return false;
7155                 libs = strip(libs);
7156                 }
7157             }
7158         return true;
7159         }
7161 private:
7163     String command;
7164     String fileName;
7165     String defFileName;
7166     String impFileName;
7167     FileSet fileSet;
7168     String libs;
7170 };
7174 /**
7175  * Run the "ar" command to archive .o's into a .a
7176  */
7177 class TaskStaticLib : public Task
7179 public:
7181     TaskStaticLib(MakeBase &par) : Task(par)
7182         {
7183         type = TASK_STATICLIB; name = "staticlib";
7184         command = "ar crv";
7185         }
7187     virtual ~TaskStaticLib()
7188         {}
7190     virtual bool execute()
7191         {
7192         //trace("###########HERE %d", fileSet.size());
7193         bool doit = false;
7194         
7195         String fullOut = parent.resolve(fileName);
7196         //trace("ar fullout: %s", fullOut.c_str());
7197         
7198         if (!listFiles(parent, fileSet))
7199             return false;
7200         String fileSetDir = fileSet.getDirectory();
7202         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7203             {
7204             String fname;
7205             if (fileSetDir.size()>0)
7206                 {
7207                 fname.append(fileSetDir);
7208                 fname.append("/");
7209                 }
7210             fname.append(fileSet[i]);
7211             String fullName = parent.resolve(fname);
7212             //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
7213             if (isNewerThan(fullName, fullOut))
7214                 doit = true;
7215             }
7216         //trace("Needs it:%d", doit);
7217         if (!doit)
7218             {
7219             return true;
7220             }
7222         String cmd = command;
7223         cmd.append(" ");
7224         cmd.append(fullOut);
7225         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7226             {
7227             String fname;
7228             if (fileSetDir.size()>0)
7229                 {
7230                 fname.append(fileSetDir);
7231                 fname.append("/");
7232                 }
7233             fname.append(fileSet[i]);
7234             String fullName = parent.resolve(fname);
7236             cmd.append(" ");
7237             cmd.append(fullName);
7238             }
7240         String outString, errString;
7241         if (!executeCommand(cmd.c_str(), "", outString, errString))
7242             {
7243             error("<staticlib> problem: %s", errString.c_str());
7244             return false;
7245             }
7247         return true;
7248         }
7251     virtual bool parse(Element *elem)
7252         {
7253         String s;
7254         if (!parent.getAttribute(elem, "command", s))
7255             return false;
7256         if (s.size()>0)
7257             command = s;
7258         if (!parent.getAttribute(elem, "file", fileName))
7259             return false;
7260             
7261         std::vector<Element *> children = elem->getChildren();
7262         for (unsigned int i=0 ; i<children.size() ; i++)
7263             {
7264             Element *child = children[i];
7265             String tagName = child->getName();
7266             if (tagName == "fileset")
7267                 {
7268                 if (!parseFileSet(child, parent, fileSet))
7269                     return false;
7270                 }
7271             }
7272         return true;
7273         }
7275 private:
7277     String command;
7278     String fileName;
7279     FileSet fileSet;
7281 };
7286 /**
7287  * Strip an executable
7288  */
7289 class TaskStrip : public Task
7291 public:
7293     TaskStrip(MakeBase &par) : Task(par)
7294         { type = TASK_STRIP; name = "strip"; }
7296     virtual ~TaskStrip()
7297         {}
7299     virtual bool execute()
7300         {
7301         String fullName = parent.resolve(fileName);
7302         //trace("fullDir:%s", fullDir.c_str());
7303         String cmd;
7304         String outbuf, errbuf;
7306         if (symFileName.size()>0)
7307             {
7308             String symFullName = parent.resolve(symFileName);
7309             cmd = "objcopy --only-keep-debug ";
7310             cmd.append(getNativePath(fullName));
7311             cmd.append(" ");
7312             cmd.append(getNativePath(symFullName));
7313             if (!executeCommand(cmd, "", outbuf, errbuf))
7314                 {
7315                 error("<strip> symbol file failed : %s", errbuf.c_str());
7316                 return false;
7317                 }
7318             }
7319             
7320         cmd = "strip ";
7321         cmd.append(getNativePath(fullName));
7322         if (!executeCommand(cmd, "", outbuf, errbuf))
7323             {
7324             error("<strip> failed : %s", errbuf.c_str());
7325             return false;
7326             }
7327         return true;
7328         }
7330     virtual bool parse(Element *elem)
7331         {
7332         if (!parent.getAttribute(elem, "file", fileName))
7333             return false;
7334         if (!parent.getAttribute(elem, "symfile", symFileName))
7335             return false;
7336         if (fileName.size() == 0)
7337             {
7338             error("<strip> requires 'file=\"fileName\"' attribute");
7339             return false;
7340             }
7341         return true;
7342         }
7344 private:
7346     String fileName;
7347     String symFileName;
7348 };
7351 /**
7352  *
7353  */
7354 class TaskTouch : public Task
7356 public:
7358     TaskTouch(MakeBase &par) : Task(par)
7359         { type = TASK_TOUCH; name = "touch"; }
7361     virtual ~TaskTouch()
7362         {}
7364     virtual bool execute()
7365         {
7366         String fullName = parent.resolve(fileName);
7367         String nativeFile = getNativePath(fullName);
7368         if (!isRegularFile(fullName) && !isDirectory(fullName))
7369             {            
7370             // S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH
7371             int ret = creat(nativeFile.c_str(), 0666);
7372             if (ret != 0) 
7373                 {
7374                 error("<touch> could not create '%s' : %s",
7375                     nativeFile.c_str(), strerror(ret));
7376                 return false;
7377                 }
7378             return true;
7379             }
7380         int ret = utime(nativeFile.c_str(), (struct utimbuf *)0);
7381         if (ret != 0)
7382             {
7383             error("<touch> could not update the modification time for '%s' : %s",
7384                 nativeFile.c_str(), strerror(ret));
7385             return false;
7386             }
7387         return true;
7388         }
7390     virtual bool parse(Element *elem)
7391         {
7392         //trace("touch parse");
7393         if (!parent.getAttribute(elem, "file", fileName))
7394             return false;
7395         if (fileName.size() == 0)
7396             {
7397             error("<touch> requires 'file=\"fileName\"' attribute");
7398             return false;
7399             }
7400         return true;
7401         }
7403     String fileName;
7404 };
7407 /**
7408  *
7409  */
7410 class TaskTstamp : public Task
7412 public:
7414     TaskTstamp(MakeBase &par) : Task(par)
7415         { type = TASK_TSTAMP; name = "tstamp"; }
7417     virtual ~TaskTstamp()
7418         {}
7420     virtual bool execute()
7421         {
7422         return true;
7423         }
7425     virtual bool parse(Element *elem)
7426         {
7427         //trace("tstamp parse");
7428         return true;
7429         }
7430 };
7434 /**
7435  *
7436  */
7437 Task *Task::createTask(Element *elem, int lineNr)
7439     String tagName = elem->getName();
7440     //trace("task:%s", tagName.c_str());
7441     Task *task = NULL;
7442     if (tagName == "cc")
7443         task = new TaskCC(parent);
7444     else if (tagName == "copy")
7445         task = new TaskCopy(parent);
7446     else if (tagName == "delete")
7447         task = new TaskDelete(parent);
7448     else if (tagName == "jar")
7449         task = new TaskJar(parent);
7450     else if (tagName == "javac")
7451         task = new TaskJavac(parent);
7452     else if (tagName == "link")
7453         task = new TaskLink(parent);
7454     else if (tagName == "makefile")
7455         task = new TaskMakeFile(parent);
7456     else if (tagName == "mkdir")
7457         task = new TaskMkDir(parent);
7458     else if (tagName == "msgfmt")
7459         task = new TaskMsgFmt(parent);
7460     else if (tagName == "ranlib")
7461         task = new TaskRanlib(parent);
7462     else if (tagName == "rc")
7463         task = new TaskRC(parent);
7464     else if (tagName == "sharedlib")
7465         task = new TaskSharedLib(parent);
7466     else if (tagName == "staticlib")
7467         task = new TaskStaticLib(parent);
7468     else if (tagName == "strip")
7469         task = new TaskStrip(parent);
7470     else if (tagName == "touch")
7471         task = new TaskTouch(parent);
7472     else if (tagName == "tstamp")
7473         task = new TaskTstamp(parent);
7474     else
7475         {
7476         error("Unknown task '%s'", tagName.c_str());
7477         return NULL;
7478         }
7480     task->setLine(lineNr);
7482     if (!task->parse(elem))
7483         {
7484         delete task;
7485         return NULL;
7486         }
7487     return task;
7492 //########################################################################
7493 //# T A R G E T
7494 //########################################################################
7496 /**
7497  *
7498  */
7499 class Target : public MakeBase
7502 public:
7504     /**
7505      *
7506      */
7507     Target(Make &par) : parent(par)
7508         { init(); }
7510     /**
7511      *
7512      */
7513     Target(const Target &other) : parent(other.parent)
7514         { init(); assign(other); }
7516     /**
7517      *
7518      */
7519     Target &operator=(const Target &other)
7520         { init(); assign(other); return *this; }
7522     /**
7523      *
7524      */
7525     virtual ~Target()
7526         { cleanup() ; }
7529     /**
7530      *
7531      */
7532     virtual Make &getParent()
7533         { return parent; }
7535     /**
7536      *
7537      */
7538     virtual String getName()
7539         { return name; }
7541     /**
7542      *
7543      */
7544     virtual void setName(const String &val)
7545         { name = val; }
7547     /**
7548      *
7549      */
7550     virtual String getDescription()
7551         { return description; }
7553     /**
7554      *
7555      */
7556     virtual void setDescription(const String &val)
7557         { description = val; }
7559     /**
7560      *
7561      */
7562     virtual void addDependency(const String &val)
7563         { deps.push_back(val); }
7565     /**
7566      *
7567      */
7568     virtual void parseDependencies(const String &val)
7569         { deps = tokenize(val, ", "); }
7571     /**
7572      *
7573      */
7574     virtual std::vector<String> &getDependencies()
7575         { return deps; }
7577     /**
7578      *
7579      */
7580     virtual String getIf()
7581         { return ifVar; }
7583     /**
7584      *
7585      */
7586     virtual void setIf(const String &val)
7587         { ifVar = val; }
7589     /**
7590      *
7591      */
7592     virtual String getUnless()
7593         { return unlessVar; }
7595     /**
7596      *
7597      */
7598     virtual void setUnless(const String &val)
7599         { unlessVar = val; }
7601     /**
7602      *
7603      */
7604     virtual void addTask(Task *val)
7605         { tasks.push_back(val); }
7607     /**
7608      *
7609      */
7610     virtual std::vector<Task *> &getTasks()
7611         { return tasks; }
7613 private:
7615     void init()
7616         {
7617         }
7619     void cleanup()
7620         {
7621         tasks.clear();
7622         }
7624     void assign(const Target &other)
7625         {
7626         //parent      = other.parent;
7627         name        = other.name;
7628         description = other.description;
7629         ifVar       = other.ifVar;
7630         unlessVar   = other.unlessVar;
7631         deps        = other.deps;
7632         tasks       = other.tasks;
7633         }
7635     Make &parent;
7637     String name;
7639     String description;
7641     String ifVar;
7643     String unlessVar;
7645     std::vector<String> deps;
7647     std::vector<Task *> tasks;
7649 };
7658 //########################################################################
7659 //# M A K E
7660 //########################################################################
7663 /**
7664  *
7665  */
7666 class Make : public MakeBase
7669 public:
7671     /**
7672      *
7673      */
7674     Make()
7675         { init(); }
7677     /**
7678      *
7679      */
7680     Make(const Make &other)
7681         { assign(other); }
7683     /**
7684      *
7685      */
7686     Make &operator=(const Make &other)
7687         { assign(other); return *this; }
7689     /**
7690      *
7691      */
7692     virtual ~Make()
7693         { cleanup(); }
7695     /**
7696      *
7697      */
7698     virtual std::map<String, Target> &getTargets()
7699         { return targets; }
7702     /**
7703      *
7704      */
7705     virtual String version()
7706         { return BUILDTOOL_VERSION; }
7708     /**
7709      * Overload a <property>
7710      */
7711     virtual bool specifyProperty(const String &name,
7712                                  const String &value);
7714     /**
7715      *
7716      */
7717     virtual bool run();
7719     /**
7720      *
7721      */
7722     virtual bool run(const String &target);
7726 private:
7728     /**
7729      *
7730      */
7731     void init();
7733     /**
7734      *
7735      */
7736     void cleanup();
7738     /**
7739      *
7740      */
7741     void assign(const Make &other);
7743     /**
7744      *
7745      */
7746     bool executeTask(Task &task);
7749     /**
7750      *
7751      */
7752     bool executeTarget(Target &target,
7753              std::set<String> &targetsCompleted);
7756     /**
7757      *
7758      */
7759     bool execute();
7761     /**
7762      *
7763      */
7764     bool checkTargetDependencies(Target &prop,
7765                     std::vector<String> &depList);
7767     /**
7768      *
7769      */
7770     bool parsePropertyFile(const String &fileName,
7771                            const String &prefix);
7773     /**
7774      *
7775      */
7776     bool parseProperty(Element *elem);
7778     /**
7779      *
7780      */
7781     bool parseFile();
7783     /**
7784      *
7785      */
7786     std::vector<String> glob(const String &pattern);
7789     //###############
7790     //# Fields
7791     //###############
7793     String projectName;
7795     String currentTarget;
7797     String defaultTarget;
7799     String specifiedTarget;
7801     String baseDir;
7803     String description;
7804     
7805     String envAlias;
7807     //std::vector<Property> properties;
7808     
7809     std::map<String, Target> targets;
7811     std::vector<Task *> allTasks;
7812     
7813     std::map<String, String> specifiedProperties;
7815 };
7818 //########################################################################
7819 //# C L A S S  M A I N T E N A N C E
7820 //########################################################################
7822 /**
7823  *
7824  */
7825 void Make::init()
7827     uri             = "build.xml";
7828     projectName     = "";
7829     currentTarget   = "";
7830     defaultTarget   = "";
7831     specifiedTarget = "";
7832     baseDir         = "";
7833     description     = "";
7834     envAlias        = "";
7835     properties.clear();
7836     for (unsigned int i = 0 ; i < allTasks.size() ; i++)
7837         delete allTasks[i];
7838     allTasks.clear();
7843 /**
7844  *
7845  */
7846 void Make::cleanup()
7848     for (unsigned int i = 0 ; i < allTasks.size() ; i++)
7849         delete allTasks[i];
7850     allTasks.clear();
7855 /**
7856  *
7857  */
7858 void Make::assign(const Make &other)
7860     uri              = other.uri;
7861     projectName      = other.projectName;
7862     currentTarget    = other.currentTarget;
7863     defaultTarget    = other.defaultTarget;
7864     specifiedTarget  = other.specifiedTarget;
7865     baseDir          = other.baseDir;
7866     description      = other.description;
7867     properties       = other.properties;
7872 //########################################################################
7873 //# U T I L I T Y    T A S K S
7874 //########################################################################
7876 /**
7877  *  Perform a file globbing
7878  */
7879 std::vector<String> Make::glob(const String &pattern)
7881     std::vector<String> res;
7882     return res;
7886 //########################################################################
7887 //# P U B L I C    A P I
7888 //########################################################################
7892 /**
7893  *
7894  */
7895 bool Make::executeTarget(Target &target,
7896              std::set<String> &targetsCompleted)
7899     String name = target.getName();
7901     //First get any dependencies for this target
7902     std::vector<String> deps = target.getDependencies();
7903     for (unsigned int i=0 ; i<deps.size() ; i++)
7904         {
7905         String dep = deps[i];
7906         //Did we do it already?  Skip
7907         if (targetsCompleted.find(dep)!=targetsCompleted.end())
7908             continue;
7909             
7910         std::map<String, Target> &tgts =
7911                target.getParent().getTargets();
7912         std::map<String, Target>::iterator iter =
7913                tgts.find(dep);
7914         if (iter == tgts.end())
7915             {
7916             error("Target '%s' dependency '%s' not found",
7917                       name.c_str(),  dep.c_str());
7918             return false;
7919             }
7920         Target depTarget = iter->second;
7921         if (!executeTarget(depTarget, targetsCompleted))
7922             {
7923             return false;
7924             }
7925         }
7927     status("## Target : %s", name.c_str());
7929     //Now let's do the tasks
7930     std::vector<Task *> &tasks = target.getTasks();
7931     for (unsigned int i=0 ; i<tasks.size() ; i++)
7932         {
7933         Task *task = tasks[i];
7934         status("---- task : %s", task->getName().c_str());
7935         if (!task->execute())
7936             {
7937             return false;
7938             }
7939         }
7940         
7941     targetsCompleted.insert(name);
7942     
7943     return true;
7948 /**
7949  *  Main execute() method.  Start here and work
7950  *  up the dependency tree 
7951  */
7952 bool Make::execute()
7954     status("######## EXECUTE");
7956     //Determine initial target
7957     if (specifiedTarget.size()>0)
7958         {
7959         currentTarget = specifiedTarget;
7960         }
7961     else if (defaultTarget.size()>0)
7962         {
7963         currentTarget = defaultTarget;
7964         }
7965     else
7966         {
7967         error("execute: no specified or default target requested");
7968         return false;
7969         }
7971     std::map<String, Target>::iterator iter =
7972                targets.find(currentTarget);
7973     if (iter == targets.end())
7974         {
7975         error("Initial target '%s' not found",
7976                  currentTarget.c_str());
7977         return false;
7978         }
7979         
7980     //Now run
7981     Target target = iter->second;
7982     std::set<String> targetsCompleted;
7983     if (!executeTarget(target, targetsCompleted))
7984         {
7985         return false;
7986         }
7988     status("######## EXECUTE COMPLETE");
7989     return true;
7995 /**
7996  *
7997  */
7998 bool Make::checkTargetDependencies(Target &target, 
7999                             std::vector<String> &depList)
8001     String tgtName = target.getName().c_str();
8002     depList.push_back(tgtName);
8004     std::vector<String> deps = target.getDependencies();
8005     for (unsigned int i=0 ; i<deps.size() ; i++)
8006         {
8007         String dep = deps[i];
8008         //First thing entered was the starting Target
8009         if (dep == depList[0])
8010             {
8011             error("Circular dependency '%s' found at '%s'",
8012                       dep.c_str(), tgtName.c_str());
8013             std::vector<String>::iterator diter;
8014             for (diter=depList.begin() ; diter!=depList.end() ; diter++)
8015                 {
8016                 error("  %s", diter->c_str());
8017                 }
8018             return false;
8019             }
8021         std::map<String, Target> &tgts =
8022                   target.getParent().getTargets();
8023         std::map<String, Target>::iterator titer = tgts.find(dep);
8024         if (titer == tgts.end())
8025             {
8026             error("Target '%s' dependency '%s' not found",
8027                       tgtName.c_str(), dep.c_str());
8028             return false;
8029             }
8030         if (!checkTargetDependencies(titer->second, depList))
8031             {
8032             return false;
8033             }
8034         }
8035     return true;
8042 static int getword(int pos, const String &inbuf, String &result)
8044     int p = pos;
8045     int len = (int)inbuf.size();
8046     String val;
8047     while (p < len)
8048         {
8049         char ch = inbuf[p];
8050         if (!isalnum(ch) && ch!='.' && ch!='_')
8051             break;
8052         val.push_back(ch);
8053         p++;
8054         }
8055     result = val;
8056     return p;
8062 /**
8063  *
8064  */
8065 bool Make::parsePropertyFile(const String &fileName,
8066                              const String &prefix)
8068     FILE *f = fopen(fileName.c_str(), "r");
8069     if (!f)
8070         {
8071         error("could not open property file %s", fileName.c_str());
8072         return false;
8073         }
8074     int linenr = 0;
8075     while (!feof(f))
8076         {
8077         char buf[256];
8078         if (!fgets(buf, 255, f))
8079             break;
8080         linenr++;
8081         String s = buf;
8082         s = trim(s);
8083         int len = s.size();
8084         if (len == 0)
8085             continue;
8086         if (s[0] == '#')
8087             continue;
8088         String key;
8089         String val;
8090         int p = 0;
8091         int p2 = getword(p, s, key);
8092         if (p2 <= p)
8093             {
8094             error("property file %s, line %d: expected keyword",
8095                     fileName.c_str(), linenr);
8096             return false;
8097             }
8098         if (prefix.size() > 0)
8099             {
8100             key.insert(0, prefix);
8101             }
8103         //skip whitespace
8104         for (p=p2 ; p<len ; p++)
8105             if (!isspace(s[p]))
8106                 break;
8108         if (p>=len || s[p]!='=')
8109             {
8110             error("property file %s, line %d: expected '='",
8111                     fileName.c_str(), linenr);
8112             return false;
8113             }
8114         p++;
8116         //skip whitespace
8117         for ( ; p<len ; p++)
8118             if (!isspace(s[p]))
8119                 break;
8121         /* This way expects a word after the =
8122         p2 = getword(p, s, val);
8123         if (p2 <= p)
8124             {
8125             error("property file %s, line %d: expected value",
8126                     fileName.c_str(), linenr);
8127             return false;
8128             }
8129         */
8130         // This way gets the rest of the line after the =
8131         if (p>=len)
8132             {
8133             error("property file %s, line %d: expected value",
8134                     fileName.c_str(), linenr);
8135             return false;
8136             }
8137         val = s.substr(p);
8138         if (key.size()==0)
8139             continue;
8140         //allow property to be set, even if val=""
8142         //trace("key:'%s' val:'%s'", key.c_str(), val.c_str());
8143         //See if we wanted to overload this property
8144         std::map<String, String>::iterator iter =
8145             specifiedProperties.find(key);
8146         if (iter!=specifiedProperties.end())
8147             {
8148             val = iter->second;
8149             status("overloading property '%s' = '%s'",
8150                    key.c_str(), val.c_str());
8151             }
8152         properties[key] = val;
8153         }
8154     fclose(f);
8155     return true;
8161 /**
8162  *
8163  */
8164 bool Make::parseProperty(Element *elem)
8166     std::vector<Attribute> &attrs = elem->getAttributes();
8167     for (unsigned int i=0 ; i<attrs.size() ; i++)
8168         {
8169         String attrName = attrs[i].getName();
8170         String attrVal  = attrs[i].getValue();
8172         if (attrName == "name")
8173             {
8174             String val;
8175             if (!getAttribute(elem, "value", val))
8176                 return false;
8177             if (val.size() > 0)
8178                 {
8179                 properties[attrVal] = val;
8180                 }
8181             else
8182                 {
8183                 if (!getAttribute(elem, "location", val))
8184                     return false;
8185                 //let the property exist, even if not defined
8186                 properties[attrVal] = val;
8187                 }
8188             //See if we wanted to overload this property
8189             std::map<String, String>::iterator iter =
8190                 specifiedProperties.find(attrVal);
8191             if (iter != specifiedProperties.end())
8192                 {
8193                 val = iter->second;
8194                 status("overloading property '%s' = '%s'",
8195                     attrVal.c_str(), val.c_str());
8196                 properties[attrVal] = val;
8197                 }
8198             }
8199         else if (attrName == "file")
8200             {
8201             String prefix;
8202             if (!getAttribute(elem, "prefix", prefix))
8203                 return false;
8204             if (prefix.size() > 0)
8205                 {
8206                 if (prefix[prefix.size()-1] != '.')
8207                     prefix.push_back('.');
8208                 }
8209             if (!parsePropertyFile(attrName, prefix))
8210                 return false;
8211             }
8212         else if (attrName == "environment")
8213             {
8214             if (envAlias.size() > 0)
8215                 {
8216                 error("environment property can only be set once");
8217                 return false;
8218                 }
8219             envAlias = attrVal;
8220             }
8221         }
8223     return true;
8229 /**
8230  *
8231  */
8232 bool Make::parseFile()
8234     status("######## PARSE : %s", uri.getPath().c_str());
8236     setLine(0);
8238     Parser parser;
8239     Element *root = parser.parseFile(uri.getNativePath());
8240     if (!root)
8241         {
8242         error("Could not open %s for reading",
8243               uri.getNativePath().c_str());
8244         return false;
8245         }
8246     
8247     setLine(root->getLine());
8249     if (root->getChildren().size()==0 ||
8250         root->getChildren()[0]->getName()!="project")
8251         {
8252         error("Main xml element should be <project>");
8253         delete root;
8254         return false;
8255         }
8257     //########## Project attributes
8258     Element *project = root->getChildren()[0];
8259     String s = project->getAttribute("name");
8260     if (s.size() > 0)
8261         projectName = s;
8262     s = project->getAttribute("default");
8263     if (s.size() > 0)
8264         defaultTarget = s;
8265     s = project->getAttribute("basedir");
8266     if (s.size() > 0)
8267         baseDir = s;
8269     //######### PARSE MEMBERS
8270     std::vector<Element *> children = project->getChildren();
8271     for (unsigned int i=0 ; i<children.size() ; i++)
8272         {
8273         Element *elem = children[i];
8274         setLine(elem->getLine());
8275         String tagName = elem->getName();
8277         //########## DESCRIPTION
8278         if (tagName == "description")
8279             {
8280             description = parser.trim(elem->getValue());
8281             }
8283         //######### PROPERTY
8284         else if (tagName == "property")
8285             {
8286             if (!parseProperty(elem))
8287                 return false;
8288             }
8290         //######### TARGET
8291         else if (tagName == "target")
8292             {
8293             String tname   = elem->getAttribute("name");
8294             String tdesc   = elem->getAttribute("description");
8295             String tdeps   = elem->getAttribute("depends");
8296             String tif     = elem->getAttribute("if");
8297             String tunless = elem->getAttribute("unless");
8298             Target target(*this);
8299             target.setName(tname);
8300             target.setDescription(tdesc);
8301             target.parseDependencies(tdeps);
8302             target.setIf(tif);
8303             target.setUnless(tunless);
8304             std::vector<Element *> telems = elem->getChildren();
8305             for (unsigned int i=0 ; i<telems.size() ; i++)
8306                 {
8307                 Element *telem = telems[i];
8308                 Task breeder(*this);
8309                 Task *task = breeder.createTask(telem, telem->getLine());
8310                 if (!task)
8311                     return false;
8312                 allTasks.push_back(task);
8313                 target.addTask(task);
8314                 }
8316             //Check name
8317             if (tname.size() == 0)
8318                 {
8319                 error("no name for target");
8320                 return false;
8321                 }
8322             //Check for duplicate name
8323             if (targets.find(tname) != targets.end())
8324                 {
8325                 error("target '%s' already defined", tname.c_str());
8326                 return false;
8327                 }
8328             //more work than targets[tname]=target, but avoids default allocator
8329             targets.insert(std::make_pair<String, Target>(tname, target));
8330             }
8331         //######### none of the above
8332         else
8333             {
8334             error("unknown toplevel tag: <%s>", tagName.c_str());
8335             return false;
8336             }
8338         }
8340     std::map<String, Target>::iterator iter;
8341     for (iter = targets.begin() ; iter!= targets.end() ; iter++)
8342         {
8343         Target tgt = iter->second;
8344         std::vector<String> depList;
8345         if (!checkTargetDependencies(tgt, depList))
8346             {
8347             return false;
8348             }
8349         }
8352     delete root;
8353     status("######## PARSE COMPLETE");
8354     return true;
8358 /**
8359  * Overload a <property>
8360  */
8361 bool Make::specifyProperty(const String &name, const String &value)
8363     if (specifiedProperties.find(name) != specifiedProperties.end())
8364         {
8365         error("Property %s already specified", name.c_str());
8366         return false;
8367         }
8368     specifiedProperties[name] = value;
8369     return true;
8374 /**
8375  *
8376  */
8377 bool Make::run()
8379     if (!parseFile())
8380         return false;
8381         
8382     if (!execute())
8383         return false;
8385     return true;
8391 /**
8392  * Get a formatted MM:SS.sss time elapsed string
8393  */ 
8394 static String
8395 timeDiffString(struct timeval &x, struct timeval &y)
8397     long microsX  = x.tv_usec;
8398     long secondsX = x.tv_sec;
8399     long microsY  = y.tv_usec;
8400     long secondsY = y.tv_sec;
8401     if (microsX < microsY)
8402         {
8403         microsX += 1000000;
8404         secondsX -= 1;
8405         }
8407     int seconds = (int)(secondsX - secondsY);
8408     int millis  = (int)((microsX - microsY)/1000);
8410     int minutes = seconds/60;
8411     seconds -= minutes*60;
8412     char buf[80];
8413     snprintf(buf, 79, "%dm %d.%03ds", minutes, seconds, millis);
8414     String ret = buf;
8415     return ret;
8416     
8419 /**
8420  *
8421  */
8422 bool Make::run(const String &target)
8424     status("####################################################");
8425     status("#   %s", version().c_str());
8426     status("####################################################");
8427     struct timeval timeStart, timeEnd;
8428     ::gettimeofday(&timeStart, NULL);
8429     specifiedTarget = target;
8430     if (!run())
8431         return false;
8432     ::gettimeofday(&timeEnd, NULL);
8433     String timeStr = timeDiffString(timeEnd, timeStart);
8434     status("####################################################");
8435     status("#   BuildTool Completed : %s", timeStr.c_str());
8436     status("####################################################");
8437     return true;
8446 }// namespace buildtool
8447 //########################################################################
8448 //# M A I N
8449 //########################################################################
8451 typedef buildtool::String String;
8453 /**
8454  *  Format an error message in printf() style
8455  */
8456 static void error(char *fmt, ...)
8458     va_list ap;
8459     va_start(ap, fmt);
8460     fprintf(stderr, "BuildTool error: ");
8461     vfprintf(stderr, fmt, ap);
8462     fprintf(stderr, "\n");
8463     va_end(ap);
8467 static bool parseProperty(const String &s, String &name, String &val)
8469     int len = s.size();
8470     int i;
8471     for (i=0 ; i<len ; i++)
8472         {
8473         char ch = s[i];
8474         if (ch == '=')
8475             break;
8476         name.push_back(ch);
8477         }
8478     if (i>=len || s[i]!='=')
8479         {
8480         error("property requires -Dname=value");
8481         return false;
8482         }
8483     i++;
8484     for ( ; i<len ; i++)
8485         {
8486         char ch = s[i];
8487         val.push_back(ch);
8488         }
8489     return true;
8493 /**
8494  * Compare a buffer with a key, for the length of the key
8495  */
8496 static bool sequ(const String &buf, char *key)
8498     int len = buf.size();
8499     for (int i=0 ; key[i] && i<len ; i++)
8500         {
8501         if (key[i] != buf[i])
8502             return false;
8503         }        
8504     return true;
8507 static void usage(int argc, char **argv)
8509     printf("usage:\n");
8510     printf("   %s [options] [target]\n", argv[0]);
8511     printf("Options:\n");
8512     printf("  -help, -h              print this message\n");
8513     printf("  -version               print the version information and exit\n");
8514     printf("  -file <file>           use given buildfile\n");
8515     printf("  -f <file>                 ''\n");
8516     printf("  -D<property>=<value>   use value for given property\n");
8522 /**
8523  * Parse the command-line args, get our options,
8524  * and run this thing
8525  */   
8526 static bool parseOptions(int argc, char **argv)
8528     if (argc < 1)
8529         {
8530         error("Cannot parse arguments");
8531         return false;
8532         }
8534     buildtool::Make make;
8536     String target;
8538     //char *progName = argv[0];
8539     for (int i=1 ; i<argc ; i++)
8540         {
8541         String arg = argv[i];
8542         if (arg.size()>1 && arg[0]=='-')
8543             {
8544             if (arg == "-h" || arg == "-help")
8545                 {
8546                 usage(argc,argv);
8547                 return true;
8548                 }
8549             else if (arg == "-version")
8550                 {
8551                 printf("%s", make.version().c_str());
8552                 return true;
8553                 }
8554             else if (arg == "-f" || arg == "-file")
8555                 {
8556                 if (i>=argc)
8557                    {
8558                    usage(argc, argv);
8559                    return false;
8560                    }
8561                 i++; //eat option
8562                 make.setURI(argv[i]);
8563                 }
8564             else if (arg.size()>2 && sequ(arg, "-D"))
8565                 {
8566                 String s = arg.substr(2, s.size());
8567                 String name, value;
8568                 if (!parseProperty(s, name, value))
8569                    {
8570                    usage(argc, argv);
8571                    return false;
8572                    }
8573                 if (!make.specifyProperty(name, value))
8574                     return false;
8575                 }
8576             else
8577                 {
8578                 error("Unknown option:%s", arg.c_str());
8579                 return false;
8580                 }
8581             }
8582         else
8583             {
8584             if (target.size()>0)
8585                 {
8586                 error("only one initial target");
8587                 usage(argc, argv);
8588                 return false;
8589                 }
8590             target = arg;
8591             }
8592         }
8594     //We have the options.  Now execute them
8595     if (!make.run(target))
8596         return false;
8598     return true;
8604 /*
8605 static bool runMake()
8607     buildtool::Make make;
8608     if (!make.run())
8609         return false;
8610     return true;
8614 static bool pkgConfigTest()
8616     buildtool::PkgConfig pkgConfig;
8617     if (!pkgConfig.readFile("gtk+-2.0.pc"))
8618         return false;
8619     return true;
8624 static bool depTest()
8626     buildtool::DepTool deptool;
8627     deptool.setSourceDirectory("/dev/ink/inkscape/src");
8628     if (!deptool.generateDependencies("build.dep"))
8629         return false;
8630     std::vector<buildtool::FileRec> res =
8631            deptool.loadDepFile("build.dep");
8632     if (res.size() == 0)
8633         return false;
8634     return true;
8637 static bool popenTest()
8639     buildtool::Make make;
8640     buildtool::String out, err;
8641     bool ret = make.executeCommand("gcc xx.cpp", "", out, err);
8642     printf("Popen test:%d '%s' '%s'\n", ret, out.c_str(), err.c_str());
8643     return true;
8647 static bool propFileTest()
8649     buildtool::Make make;
8650     make.parsePropertyFile("test.prop", "test.");
8651     return true;
8653 */
8655 int main(int argc, char **argv)
8658     if (!parseOptions(argc, argv))
8659         return 1;
8660     /*
8661     if (!popenTest())
8662         return 1;
8664     if (!depTest())
8665         return 1;
8666     if (!propFileTest())
8667         return 1;
8668     if (runMake())
8669         return 1;
8670     */
8671     return 0;
8675 //########################################################################
8676 //# E N D 
8677 //########################################################################