Code

two picking optimizations: 1 use our canvas' viewbox so that invisible segments can...
[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.7, 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;
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,
5063                            FileRec *include,
5064                            int depth);
5066     /**
5067      *
5068      */
5069     String sourceDir;
5071     /**
5072      *
5073      */
5074     std::vector<String> fileList;
5076     /**
5077      *
5078      */
5079     std::vector<String> directories;
5081     /**
5082      * A list of all files which will be processed for
5083      * dependencies.  This is the only list that has the actual
5084      * records.  All other lists have pointers to these records.     
5085      */
5086     std::map<String, FileRec *> allFiles;
5088     /**
5089      * The list of .o files, and the
5090      * dependencies upon them.
5091      */
5092     std::map<String, FileRec *> depFiles;
5094     int depFileSize;
5095     char *depFileBuf;
5097     static const int readBufSize = 8192;
5098     char readBuf[8193];//byte larger
5100 };
5106 /**
5107  *  Clean up after processing.  Called by the destructor, but should
5108  *  also be called before the object is reused.
5109  */
5110 void DepTool::init()
5112     sourceDir = ".";
5114     fileList.clear();
5115     directories.clear();
5116     
5117     //clear refs
5118     depFiles.clear();
5119     //clear records
5120     std::map<String, FileRec *>::iterator iter;
5121     for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5122          delete iter->second;
5124     allFiles.clear(); 
5131 /**
5132  *  Parse a full path name into path, base name, and suffix
5133  */
5134 void DepTool::parseName(const String &fullname,
5135                         String &path,
5136                         String &basename,
5137                         String &suffix)
5139     if (fullname.size() < 2)
5140         return;
5142     unsigned int pos = fullname.find_last_of('/');
5143     if (pos != fullname.npos && pos<fullname.size()-1)
5144         {
5145         path = fullname.substr(0, pos);
5146         pos++;
5147         basename = fullname.substr(pos, fullname.size()-pos);
5148         }
5149     else
5150         {
5151         path = "";
5152         basename = fullname;
5153         }
5155     pos = basename.find_last_of('.');
5156     if (pos != basename.npos && pos<basename.size()-1)
5157         {
5158         suffix   = basename.substr(pos+1, basename.size()-pos-1);
5159         basename = basename.substr(0, pos);
5160         }
5162     //trace("parsename:%s %s %s", path.c_str(),
5163     //        basename.c_str(), suffix.c_str()); 
5168 /**
5169  *  Generate our internal file list.
5170  */
5171 bool DepTool::createFileList()
5174     for (unsigned int i=0 ; i<fileList.size() ; i++)
5175         {
5176         String fileName = fileList[i];
5177         //trace("## FileName:%s", fileName.c_str());
5178         String path;
5179         String basename;
5180         String sfx;
5181         parseName(fileName, path, basename, sfx);
5182         if (sfx == "cpp" || sfx == "c" || sfx == "cxx"   ||
5183             sfx == "cc" || sfx == "CC")
5184             {
5185             FileRec *fe         = new FileRec(FileRec::CFILE);
5186             fe->path            = path;
5187             fe->baseName        = basename;
5188             fe->suffix          = sfx;
5189             allFiles[fileName]  = fe;
5190             }
5191         else if (sfx == "h"   ||  sfx == "hh"  ||
5192                  sfx == "hpp" ||  sfx == "hxx")
5193             {
5194             FileRec *fe         = new FileRec(FileRec::HFILE);
5195             fe->path            = path;
5196             fe->baseName        = basename;
5197             fe->suffix          = sfx;
5198             allFiles[fileName]  = fe;
5199             }
5200         }
5202     if (!listDirectories(sourceDir, "", directories))
5203         return false;
5204         
5205     return true;
5212 /**
5213  * Get a character from the buffer at pos.  If out of range,
5214  * return -1 for safety
5215  */
5216 int DepTool::get(int pos)
5218     if (pos>depFileSize)
5219         return -1;
5220     return depFileBuf[pos];
5225 /**
5226  *  Skip over all whitespace characters beginning at pos.  Return
5227  *  the position of the first non-whitespace character.
5228  */
5229 int DepTool::skipwhite(int pos)
5231     while (pos < depFileSize)
5232         {
5233         int ch = get(pos);
5234         if (ch < 0)
5235             break;
5236         if (!isspace(ch))
5237             break;
5238         pos++;
5239         }
5240     return pos;
5244 /**
5245  *  Parse the buffer beginning at pos, for a word.  Fill
5246  *  'ret' with the result.  Return the position after the
5247  *  word.
5248  */
5249 int DepTool::getword(int pos, String &ret)
5251     while (pos < depFileSize)
5252         {
5253         int ch = get(pos);
5254         if (ch < 0)
5255             break;
5256         if (isspace(ch))
5257             break;
5258         ret.push_back((char)ch);
5259         pos++;
5260         }
5261     return pos;
5264 /**
5265  * Return whether the sequence of characters in the buffer
5266  * beginning at pos match the key,  for the length of the key
5267  */
5268 bool DepTool::sequ(int pos, char *key)
5270     while (*key)
5271         {
5272         if (*key != get(pos))
5273             return false;
5274         key++; pos++;
5275         }
5276     return true;
5281 /**
5282  *  Add an include file name to a file record.  If the name
5283  *  is not found in allFiles explicitly, try prepending include
5284  *  directory names to it and try again.
5285  */
5286 bool DepTool::addIncludeFile(FileRec *frec, const String &iname)
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         //look in other dirs
5302         std::vector<String>::iterator diter;
5303         for (diter=directories.begin() ;
5304              diter!=directories.end() ; diter++)
5305             {
5306             String dfname = *diter;
5307             dfname.append("/");
5308             dfname.append(iname);
5309             iter = allFiles.find(dfname);
5310             if (iter != allFiles.end())
5311                 {
5312                 FileRec *other = iter->second;
5313                 //trace("other: '%s'", iname.c_str());
5314                 frec->files[dfname] = other;
5315                 return true;
5316                 }
5317             }
5318         }
5319     return true;
5324 /**
5325  *  Lightly parse a file to find the #include directives.  Do
5326  *  a bit of state machine stuff to make sure that the directive
5327  *  is valid.  (Like not in a comment).
5328  */
5329 bool DepTool::scanFile(const String &fname, FileRec *frec)
5331     String fileName;
5332     if (sourceDir.size() > 0)
5333         {
5334         fileName.append(sourceDir);
5335         fileName.append("/");
5336         }
5337     fileName.append(fname);
5338     String nativeName = getNativePath(fileName);
5339     FILE *f = fopen(nativeName.c_str(), "r");
5340     if (!f)
5341         {
5342         error("Could not open '%s' for reading", fname.c_str());
5343         return false;
5344         }
5345     String buf;
5346     while (!feof(f))
5347         {
5348         int len = fread(readBuf, 1, readBufSize, f);
5349         readBuf[len] = '\0';
5350         buf.append(readBuf);
5351         }
5352     fclose(f);
5354     depFileSize = buf.size();
5355     depFileBuf  = (char *)buf.c_str();
5356     int pos = 0;
5359     while (pos < depFileSize)
5360         {
5361         //trace("p:%c", get(pos));
5363         //# Block comment
5364         if (get(pos) == '/' && get(pos+1) == '*')
5365             {
5366             pos += 2;
5367             while (pos < depFileSize)
5368                 {
5369                 if (get(pos) == '*' && get(pos+1) == '/')
5370                     {
5371                     pos += 2;
5372                     break;
5373                     }
5374                 else
5375                     pos++;
5376                 }
5377             }
5378         //# Line comment
5379         else if (get(pos) == '/' && get(pos+1) == '/')
5380             {
5381             pos += 2;
5382             while (pos < depFileSize)
5383                 {
5384                 if (get(pos) == '\n')
5385                     {
5386                     pos++;
5387                     break;
5388                     }
5389                 else
5390                     pos++;
5391                 }
5392             }
5393         //# #include! yaay
5394         else if (sequ(pos, "#include"))
5395             {
5396             pos += 8;
5397             pos = skipwhite(pos);
5398             String iname;
5399             pos = getword(pos, iname);
5400             if (iname.size()>2)
5401                 {
5402                 iname = iname.substr(1, iname.size()-2);
5403                 addIncludeFile(frec, iname);
5404                 }
5405             }
5406         else
5407             {
5408             pos++;
5409             }
5410         }
5412     return true;
5417 /**
5418  *  Recursively check include lists to find all files in allFiles to which
5419  *  a given file is dependent.
5420  */
5421 bool DepTool::processDependency(FileRec *ofile,
5422                              FileRec *include,
5423                              int depth)
5425     std::map<String, FileRec *>::iterator iter;
5426     for (iter=include->files.begin() ; iter!=include->files.end() ; iter++)
5427         {
5428         String fname  = iter->first;
5429         if (ofile->files.find(fname) != ofile->files.end())
5430             {
5431             //trace("file '%s' already seen", fname.c_str());
5432             continue;
5433             }
5434         FileRec *child  = iter->second;
5435         ofile->files[fname] = child;
5436       
5437         processDependency(ofile, child, depth+1);
5438         }
5441     return true;
5448 /**
5449  *  Generate the file dependency list.
5450  */
5451 bool DepTool::generateDependencies()
5453     std::map<String, FileRec *>::iterator iter;
5454     //# First pass.  Scan for all includes
5455     for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5456         {
5457         FileRec *frec = iter->second;
5458         if (!scanFile(iter->first, frec))
5459             {
5460             //quit?
5461             }
5462         }
5464     //# Second pass.  Scan for all includes
5465     for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5466         {
5467         FileRec *include = iter->second;
5468         if (include->type == FileRec::CFILE)
5469             {
5470             String cFileName = iter->first;
5471             FileRec *ofile      = new FileRec(FileRec::OFILE);
5472             ofile->path         = include->path;
5473             ofile->baseName     = include->baseName;
5474             ofile->suffix       = include->suffix;
5475             String fname     = include->path;
5476             if (fname.size()>0)
5477                 fname.append("/");
5478             fname.append(include->baseName);
5479             fname.append(".o");
5480             depFiles[fname]    = ofile;
5481             //add the .c file first?   no, don't
5482             //ofile->files[cFileName] = include;
5483             
5484             //trace("ofile:%s", fname.c_str());
5486             processDependency(ofile, include, 0);
5487             }
5488         }
5490       
5491     return true;
5496 /**
5497  *  High-level call to generate deps and optionally save them
5498  */
5499 bool DepTool::generateDependencies(const String &fileName)
5501     if (!createFileList())
5502         return false;
5503     if (!generateDependencies())
5504         return false;
5505     if (!saveDepFile(fileName))
5506         return false;
5507     return true;
5511 /**
5512  *   This saves the dependency cache.
5513  */
5514 bool DepTool::saveDepFile(const String &fileName)
5516     time_t tim;
5517     time(&tim);
5519     FILE *f = fopen(fileName.c_str(), "w");
5520     if (!f)
5521         {
5522         trace("cannot open '%s' for writing", fileName.c_str());
5523         }
5524     fprintf(f, "<?xml version='1.0'?>\n");
5525     fprintf(f, "<!--\n");
5526     fprintf(f, "########################################################\n");
5527     fprintf(f, "## File: build.dep\n");
5528     fprintf(f, "## Generated by BuildTool at :%s", ctime(&tim));
5529     fprintf(f, "########################################################\n");
5530     fprintf(f, "-->\n");
5532     fprintf(f, "<dependencies source='%s'>\n\n", sourceDir.c_str());
5533     std::map<String, FileRec *>::iterator iter;
5534     for (iter=depFiles.begin() ; iter!=depFiles.end() ; iter++)
5535         {
5536         FileRec *frec = iter->second;
5537         if (frec->type == FileRec::OFILE)
5538             {
5539             fprintf(f, "<object path='%s' name='%s' suffix='%s'>\n",
5540                  frec->path.c_str(), frec->baseName.c_str(), frec->suffix.c_str());
5541             std::map<String, FileRec *>::iterator citer;
5542             for (citer=frec->files.begin() ; citer!=frec->files.end() ; citer++)
5543                 {
5544                 String cfname = citer->first;
5545                 fprintf(f, "    <dep name='%s'/>\n", cfname.c_str());
5546                 }
5547             fprintf(f, "</object>\n\n");
5548             }
5549         }
5551     fprintf(f, "</dependencies>\n");
5552     fprintf(f, "\n");
5553     fprintf(f, "<!--\n");
5554     fprintf(f, "########################################################\n");
5555     fprintf(f, "## E N D\n");
5556     fprintf(f, "########################################################\n");
5557     fprintf(f, "-->\n");
5559     fclose(f);
5561     return true;
5567 /**
5568  *   This loads the dependency cache.
5569  */
5570 std::vector<DepRec> DepTool::loadDepFile(const String &depFile)
5572     std::vector<DepRec> result;
5573     
5574     Parser parser;
5575     Element *root = parser.parseFile(depFile.c_str());
5576     if (!root)
5577         {
5578         //error("Could not open %s for reading", depFile.c_str());
5579         return result;
5580         }
5582     if (root->getChildren().size()==0 ||
5583         root->getChildren()[0]->getName()!="dependencies")
5584         {
5585         error("Main xml element should be <dependencies>");
5586         delete root;
5587         return result;
5588         }
5590     //########## Start parsing
5591     Element *depList = root->getChildren()[0];
5593     std::vector<Element *> objects = depList->getChildren();
5594     for (unsigned int i=0 ; i<objects.size() ; i++)
5595         {
5596         Element *objectElem = objects[i];
5597         String tagName = objectElem->getName();
5598         if (tagName == "object")
5599             {
5600             String objName   = objectElem->getAttribute("name");
5601              //trace("object:%s", objName.c_str());
5602             DepRec depObject(objName);
5603             depObject.path   = objectElem->getAttribute("path");
5604             depObject.suffix = objectElem->getAttribute("suffix");
5605             //########## DESCRIPTION
5606             std::vector<Element *> depElems = objectElem->getChildren();
5607             for (unsigned int i=0 ; i<depElems.size() ; i++)
5608                 {
5609                 Element *depElem = depElems[i];
5610                 tagName = depElem->getName();
5611                 if (tagName == "dep")
5612                     {
5613                     String depName = depElem->getAttribute("name");
5614                     //trace("    dep:%s", depName.c_str());
5615                     depObject.files.push_back(depName);
5616                     }
5617                 }
5618             //Insert into the result list, in a sorted manner
5619             bool inserted = false;
5620             std::vector<DepRec>::iterator iter;
5621             for (iter = result.begin() ; iter != result.end() ; iter++)
5622                 {
5623                 String vpath = iter->path;
5624                 vpath.append("/");
5625                 vpath.append(iter->name);
5626                 String opath = depObject.path;
5627                 opath.append("/");
5628                 opath.append(depObject.name);
5629                 if (vpath > opath)
5630                     {
5631                     inserted = true;
5632                     iter = result.insert(iter, depObject);
5633                     break;
5634                     }
5635                 }
5636             if (!inserted)
5637                 result.push_back(depObject);
5638             }
5639         }
5641     delete root;
5643     return result;
5647 /**
5648  *   This loads the dependency cache.
5649  */
5650 std::vector<DepRec> DepTool::getDepFile(const String &depFile,
5651                    bool forceRefresh)
5653     std::vector<DepRec> result;
5654     if (forceRefresh)
5655         {
5656         generateDependencies(depFile);
5657         result = loadDepFile(depFile);
5658         }
5659     else
5660         {
5661         //try once
5662         result = loadDepFile(depFile);
5663         if (result.size() == 0)
5664             {
5665             //fail? try again
5666             generateDependencies(depFile);
5667             result = loadDepFile(depFile);
5668             }
5669         }
5670     return result;
5676 //########################################################################
5677 //# T A S K
5678 //########################################################################
5679 //forward decl
5680 class Target;
5681 class Make;
5683 /**
5684  *
5685  */
5686 class Task : public MakeBase
5689 public:
5691     typedef enum
5692         {
5693         TASK_NONE,
5694         TASK_CC,
5695         TASK_COPY,
5696         TASK_DELETE,
5697         TASK_JAR,
5698         TASK_JAVAC,
5699         TASK_LINK,
5700         TASK_MAKEFILE,
5701         TASK_MKDIR,
5702         TASK_MSGFMT,
5703         TASK_RANLIB,
5704         TASK_RC,
5705         TASK_SHAREDLIB,
5706         TASK_STATICLIB,
5707         TASK_STRIP,
5708         TASK_TOUCH,
5709         TASK_TSTAMP
5710         } TaskType;
5711         
5713     /**
5714      *
5715      */
5716     Task(MakeBase &par) : parent(par)
5717         { init(); }
5719     /**
5720      *
5721      */
5722     Task(const Task &other) : parent(other.parent)
5723         { init(); assign(other); }
5725     /**
5726      *
5727      */
5728     Task &operator=(const Task &other)
5729         { assign(other); return *this; }
5731     /**
5732      *
5733      */
5734     virtual ~Task()
5735         { }
5738     /**
5739      *
5740      */
5741     virtual MakeBase &getParent()
5742         { return parent; }
5744      /**
5745      *
5746      */
5747     virtual int  getType()
5748         { return type; }
5750     /**
5751      *
5752      */
5753     virtual void setType(int val)
5754         { type = val; }
5756     /**
5757      *
5758      */
5759     virtual String getName()
5760         { return name; }
5762     /**
5763      *
5764      */
5765     virtual bool execute()
5766         { return true; }
5768     /**
5769      *
5770      */
5771     virtual bool parse(Element *elem)
5772         { return true; }
5774     /**
5775      *
5776      */
5777     Task *createTask(Element *elem, int lineNr);
5780 protected:
5782     void init()
5783         {
5784         type = TASK_NONE;
5785         name = "none";
5786         }
5788     void assign(const Task &other)
5789         {
5790         type = other.type;
5791         name = other.name;
5792         }
5793         
5794     String getAttribute(Element *elem, const String &attrName)
5795         {
5796         String str;
5797         return str;
5798         }
5800     MakeBase &parent;
5802     int type;
5804     String name;
5805 };
5809 /**
5810  * This task runs the C/C++ compiler.  The compiler is invoked
5811  * for all .c or .cpp files which are newer than their correcsponding
5812  * .o files.  
5813  */
5814 class TaskCC : public Task
5816 public:
5818     TaskCC(MakeBase &par) : Task(par)
5819         {
5820         type = TASK_CC; name = "cc";
5821         ccCommand   = "gcc";
5822         cxxCommand  = "g++";
5823         source      = ".";
5824         dest        = ".";
5825         flags       = "";
5826         defines     = "";
5827         includes    = "";
5828         fileSet.clear();
5829         }
5831     virtual ~TaskCC()
5832         {}
5834     virtual bool needsCompiling(const DepRec &depRec,
5835               const String &src, const String &dest)
5836         {
5837         return false;
5838         }
5840     virtual bool execute()
5841         {
5842         if (!listFiles(parent, fileSet))
5843             return false;
5844             
5845         FILE *f = NULL;
5846         f = fopen("compile.lst", "w");
5848         bool refreshCache = false;
5849         String fullName = parent.resolve("build.dep");
5850         if (isNewerThan(parent.getURI().getPath(), fullName))
5851             {
5852             status("          : regenerating C/C++ dependency cache");
5853             refreshCache = true;
5854             }
5856         DepTool depTool;
5857         depTool.setSourceDirectory(source);
5858         depTool.setFileList(fileSet.getFiles());
5859         std::vector<DepRec> deps =
5860              depTool.getDepFile("build.dep", refreshCache);
5861         
5862         String incs;
5863         incs.append("-I");
5864         incs.append(parent.resolve("."));
5865         incs.append(" ");
5866         if (includes.size()>0)
5867             {
5868             incs.append(includes);
5869             incs.append(" ");
5870             }
5871         std::set<String> paths;
5872         std::vector<DepRec>::iterator viter;
5873         for (viter=deps.begin() ; viter!=deps.end() ; viter++)
5874             {
5875             DepRec dep = *viter;
5876             if (dep.path.size()>0)
5877                 paths.insert(dep.path);
5878             }
5879         if (source.size()>0)
5880             {
5881             incs.append(" -I");
5882             incs.append(parent.resolve(source));
5883             incs.append(" ");
5884             }
5885         std::set<String>::iterator setIter;
5886         for (setIter=paths.begin() ; setIter!=paths.end() ; setIter++)
5887             {
5888             incs.append(" -I");
5889             String dname;
5890             if (source.size()>0)
5891                 {
5892                 dname.append(source);
5893                 dname.append("/");
5894                 }
5895             dname.append(*setIter);
5896             incs.append(parent.resolve(dname));
5897             }
5898         std::vector<String> cfiles;
5899         for (viter=deps.begin() ; viter!=deps.end() ; viter++)
5900             {
5901             DepRec dep = *viter;
5903             //## Select command
5904             String sfx = dep.suffix;
5905             String command = ccCommand;
5906             if (sfx == "cpp" || sfx == "c++" || sfx == "cc"
5907                  || sfx == "CC")
5908                 command = cxxCommand;
5909  
5910             //## Make paths
5911             String destPath = dest;
5912             String srcPath  = source;
5913             if (dep.path.size()>0)
5914                 {
5915                 destPath.append("/");
5916                 destPath.append(dep.path);
5917                 srcPath.append("/");
5918                 srcPath.append(dep.path);
5919                 }
5920             //## Make sure destination directory exists
5921             if (!createDirectory(destPath))
5922                 return false;
5923                 
5924             //## Check whether it needs to be done
5925             String destName;
5926             if (destPath.size()>0)
5927                 {
5928                 destName.append(destPath);
5929                 destName.append("/");
5930                 }
5931             destName.append(dep.name);
5932             destName.append(".o");
5933             String destFullName = parent.resolve(destName);
5934             String srcName;
5935             if (srcPath.size()>0)
5936                 {
5937                 srcName.append(srcPath);
5938                 srcName.append("/");
5939                 }
5940             srcName.append(dep.name);
5941             srcName.append(".");
5942             srcName.append(dep.suffix);
5943             String srcFullName = parent.resolve(srcName);
5944             bool compileMe = false;
5945             if (isNewerThan(srcFullName, destFullName))
5946                 {
5947                 status("          : compile of %s required by %s",
5948                         destFullName.c_str(), srcFullName.c_str());
5949                 compileMe = true;
5950                 }
5951             else
5952                 {
5953                 for (unsigned int i=0 ; i<dep.files.size() ; i++)
5954                     {
5955                     String depName;
5956                     if (srcPath.size()>0)
5957                         {
5958                         depName.append(srcPath);
5959                         depName.append("/");
5960                         }
5961                     depName.append(dep.files[i]);
5962                     String depFullName = parent.resolve(depName);
5963                     if (isNewerThan(depFullName, destFullName))
5964                         {
5965                         status("          : compile of %s required by %s",
5966                                 destFullName.c_str(), depFullName.c_str());
5967                         compileMe = true;
5968                         break;
5969                         }
5970                     }
5971                 }
5972             if (!compileMe)
5973                 {
5974                 continue;
5975                 }
5977             //## Assemble the command
5978             String cmd = command;
5979             cmd.append(" -c ");
5980             cmd.append(flags);
5981             cmd.append(" ");
5982             cmd.append(defines);
5983             cmd.append(" ");
5984             cmd.append(incs);
5985             cmd.append(" ");
5986             cmd.append(srcFullName);
5987             cmd.append(" -o ");
5988             cmd.append(destFullName);
5990             //## Execute the command
5992             String outString, errString;
5993             bool ret = executeCommand(cmd.c_str(), "", outString, errString);
5995             if (f)
5996                 {
5997                 fprintf(f, "########################### File : %s\n",
5998                              srcFullName.c_str());
5999                 fprintf(f, "#### COMMAND ###\n");
6000                 int col = 0;
6001                 for (int i = 0 ; i < cmd.size() ; i++)
6002                     {
6003                     char ch = cmd[i];
6004                     if (isspace(ch)  && col > 63)
6005                         {
6006                         fputc('\n', f);
6007                         col = 0;
6008                         }
6009                     else
6010                         {
6011                         fputc(ch, f);
6012                         col++;
6013                         }
6014                     if (col > 76)
6015                         {
6016                         fputc('\n', f);
6017                         col = 0;
6018                         }
6019                     }
6020                 fprintf(f, "\n");
6021                 fprintf(f, "#### STDOUT ###\n%s\n", outString.c_str());
6022                 fprintf(f, "#### STDERR ###\n%s\n\n", errString.c_str());
6023                 }
6024             if (!ret)
6025                 {
6026                 error("problem compiling: %s", errString.c_str());
6027                 return false;
6028                 }
6029                 
6030             }
6032         if (f)
6033             {
6034             fclose(f);
6035             }
6036         
6037         return true;
6038         }
6040     virtual bool parse(Element *elem)
6041         {
6042         String s;
6043         if (!parent.getAttribute(elem, "command", s))
6044             return false;
6045         if (s.size()>0) { ccCommand = s; cxxCommand = s; }
6046         if (!parent.getAttribute(elem, "cc", s))
6047             return false;
6048         if (s.size()>0) ccCommand = s;
6049         if (!parent.getAttribute(elem, "cxx", s))
6050             return false;
6051         if (s.size()>0) cxxCommand = s;
6052         if (!parent.getAttribute(elem, "destdir", s))
6053             return false;
6054         if (s.size()>0) dest = s;
6056         std::vector<Element *> children = elem->getChildren();
6057         for (unsigned int i=0 ; i<children.size() ; i++)
6058             {
6059             Element *child = children[i];
6060             String tagName = child->getName();
6061             if (tagName == "flags")
6062                 {
6063                 if (!parent.getValue(child, flags))
6064                     return false;
6065                 flags = strip(flags);
6066                 }
6067             else if (tagName == "includes")
6068                 {
6069                 if (!parent.getValue(child, includes))
6070                     return false;
6071                 includes = strip(includes);
6072                 }
6073             else if (tagName == "defines")
6074                 {
6075                 if (!parent.getValue(child, defines))
6076                     return false;
6077                 defines = strip(defines);
6078                 }
6079             else if (tagName == "fileset")
6080                 {
6081                 if (!parseFileSet(child, parent, fileSet))
6082                     return false;
6083                 source = fileSet.getDirectory();
6084                 }
6085             }
6087         return true;
6088         }
6089         
6090 protected:
6092     String ccCommand;
6093     String cxxCommand;
6094     String source;
6095     String dest;
6096     String flags;
6097     String defines;
6098     String includes;
6099     FileSet fileSet;
6100     
6101 };
6105 /**
6106  *
6107  */
6108 class TaskCopy : public Task
6110 public:
6112     typedef enum
6113         {
6114         CP_NONE,
6115         CP_TOFILE,
6116         CP_TODIR
6117         } CopyType;
6119     TaskCopy(MakeBase &par) : Task(par)
6120         {
6121         type = TASK_COPY; name = "copy";
6122         cptype = CP_NONE;
6123         verbose = false;
6124         haveFileSet = false;
6125         }
6127     virtual ~TaskCopy()
6128         {}
6130     virtual bool execute()
6131         {
6132         switch (cptype)
6133            {
6134            case CP_TOFILE:
6135                {
6136                if (fileName.size()>0)
6137                    {
6138                    status("          : %s to %s",
6139                         fileName.c_str(), toFileName.c_str());
6140                    String fullSource = parent.resolve(fileName);
6141                    String fullDest = parent.resolve(toFileName);
6142                    //trace("copy %s to file %s", fullSource.c_str(),
6143                    //                       fullDest.c_str());
6144                    if (!isRegularFile(fullSource))
6145                        {
6146                        error("copy : file %s does not exist", fullSource.c_str());
6147                        return false;
6148                        }
6149                    if (!isNewerThan(fullSource, fullDest))
6150                        {
6151                        return true;
6152                        }
6153                    if (!copyFile(fullSource, fullDest))
6154                        return false;
6155                    status("          : 1 file copied");
6156                    }
6157                return true;
6158                }
6159            case CP_TODIR:
6160                {
6161                if (haveFileSet)
6162                    {
6163                    if (!listFiles(parent, fileSet))
6164                        return false;
6165                    String fileSetDir = fileSet.getDirectory();
6167                    status("          : %s to %s",
6168                        fileSetDir.c_str(), toDirName.c_str());
6170                    int nrFiles = 0;
6171                    for (unsigned int i=0 ; i<fileSet.size() ; i++)
6172                        {
6173                        String fileName = fileSet[i];
6175                        String sourcePath;
6176                        if (fileSetDir.size()>0)
6177                            {
6178                            sourcePath.append(fileSetDir);
6179                            sourcePath.append("/");
6180                            }
6181                        sourcePath.append(fileName);
6182                        String fullSource = parent.resolve(sourcePath);
6183                        
6184                        //Get the immediate parent directory's base name
6185                        String baseFileSetDir = fileSetDir;
6186                        unsigned int pos = baseFileSetDir.find_last_of('/');
6187                        if (pos!=baseFileSetDir.npos &&
6188                                   pos < baseFileSetDir.size()-1)
6189                            baseFileSetDir =
6190                               baseFileSetDir.substr(pos+1,
6191                                    baseFileSetDir.size());
6192                        //Now make the new path
6193                        String destPath;
6194                        if (toDirName.size()>0)
6195                            {
6196                            destPath.append(toDirName);
6197                            destPath.append("/");
6198                            }
6199                        if (baseFileSetDir.size()>0)
6200                            {
6201                            destPath.append(baseFileSetDir);
6202                            destPath.append("/");
6203                            }
6204                        destPath.append(fileName);
6205                        String fullDest = parent.resolve(destPath);
6206                        //trace("fileName:%s", fileName.c_str());
6207                        //trace("copy %s to new dir : %s", fullSource.c_str(),
6208                        //                   fullDest.c_str());
6209                        if (!isNewerThan(fullSource, fullDest))
6210                            {
6211                            //trace("copy skipping %s", fullSource.c_str());
6212                            continue;
6213                            }
6214                        if (!copyFile(fullSource, fullDest))
6215                            return false;
6216                        nrFiles++;
6217                        }
6218                    status("          : %d file(s) copied", nrFiles);
6219                    }
6220                else //file source
6221                    {
6222                    //For file->dir we want only the basename of
6223                    //the source appended to the dest dir
6224                    status("          : %s to %s", 
6225                        fileName.c_str(), toDirName.c_str());
6226                    String baseName = fileName;
6227                    unsigned int pos = baseName.find_last_of('/');
6228                    if (pos!=baseName.npos && pos<baseName.size()-1)
6229                        baseName = baseName.substr(pos+1, baseName.size());
6230                    String fullSource = parent.resolve(fileName);
6231                    String destPath;
6232                    if (toDirName.size()>0)
6233                        {
6234                        destPath.append(toDirName);
6235                        destPath.append("/");
6236                        }
6237                    destPath.append(baseName);
6238                    String fullDest = parent.resolve(destPath);
6239                    //trace("copy %s to new dir : %s", fullSource.c_str(),
6240                    //                       fullDest.c_str());
6241                    if (!isRegularFile(fullSource))
6242                        {
6243                        error("copy : file %s does not exist", fullSource.c_str());
6244                        return false;
6245                        }
6246                    if (!isNewerThan(fullSource, fullDest))
6247                        {
6248                        return true;
6249                        }
6250                    if (!copyFile(fullSource, fullDest))
6251                        return false;
6252                    status("          : 1 file copied");
6253                    }
6254                return true;
6255                }
6256            }
6257         return true;
6258         }
6261     virtual bool parse(Element *elem)
6262         {
6263         if (!parent.getAttribute(elem, "file", fileName))
6264             return false;
6265         if (!parent.getAttribute(elem, "tofile", toFileName))
6266             return false;
6267         if (toFileName.size() > 0)
6268             cptype = CP_TOFILE;
6269         if (!parent.getAttribute(elem, "todir", toDirName))
6270             return false;
6271         if (toDirName.size() > 0)
6272             cptype = CP_TODIR;
6273         String ret;
6274         if (!parent.getAttribute(elem, "verbose", ret))
6275             return false;
6276         if (ret.size()>0 && !getBool(ret, verbose))
6277             return false;
6278             
6279         haveFileSet = false;
6280         
6281         std::vector<Element *> children = elem->getChildren();
6282         for (unsigned int i=0 ; i<children.size() ; i++)
6283             {
6284             Element *child = children[i];
6285             String tagName = child->getName();
6286             if (tagName == "fileset")
6287                 {
6288                 if (!parseFileSet(child, parent, fileSet))
6289                     {
6290                     error("problem getting fileset");
6291                     return false;
6292                     }
6293                 haveFileSet = true;
6294                 }
6295             }
6297         //Perform validity checks
6298         if (fileName.size()>0 && fileSet.size()>0)
6299             {
6300             error("<copy> can only have one of : file= and <fileset>");
6301             return false;
6302             }
6303         if (toFileName.size()>0 && toDirName.size()>0)
6304             {
6305             error("<copy> can only have one of : tofile= or todir=");
6306             return false;
6307             }
6308         if (haveFileSet && toDirName.size()==0)
6309             {
6310             error("a <copy> task with a <fileset> must have : todir=");
6311             return false;
6312             }
6313         if (cptype == CP_TOFILE && fileName.size()==0)
6314             {
6315             error("<copy> tofile= must be associated with : file=");
6316             return false;
6317             }
6318         if (cptype == CP_TODIR && fileName.size()==0 && !haveFileSet)
6319             {
6320             error("<copy> todir= must be associated with : file= or <fileset>");
6321             return false;
6322             }
6324         return true;
6325         }
6326         
6327 private:
6329     int cptype;
6330     String fileName;
6331     FileSet fileSet;
6332     String toFileName;
6333     String toDirName;
6334     bool verbose;
6335     bool haveFileSet;
6336 };
6339 /**
6340  *
6341  */
6342 class TaskDelete : public Task
6344 public:
6346     typedef enum
6347         {
6348         DEL_FILE,
6349         DEL_DIR,
6350         DEL_FILESET
6351         } DeleteType;
6353     TaskDelete(MakeBase &par) : Task(par)
6354         { 
6355           type        = TASK_DELETE;
6356           name        = "delete";
6357           delType     = DEL_FILE;
6358           verbose     = false;
6359           quiet       = false;
6360           failOnError = true;
6361         }
6363     virtual ~TaskDelete()
6364         {}
6366     virtual bool execute()
6367         {
6368         struct stat finfo;
6369         switch (delType)
6370             {
6371             case DEL_FILE:
6372                 {
6373                 status("          : %s", fileName.c_str());
6374                 String fullName = parent.resolve(fileName);
6375                 char *fname = (char *)fullName.c_str();
6376                 //does not exist
6377                 if (stat(fname, &finfo)<0)
6378                     return true;
6379                 //exists but is not a regular file
6380                 if (!S_ISREG(finfo.st_mode))
6381                     {
6382                     error("<delete> failed. '%s' exists and is not a regular file",
6383                           fname);
6384                     return false;
6385                     }
6386                 if (remove(fname)<0)
6387                     {
6388                     error("<delete> failed: %s", strerror(errno));
6389                     return false;
6390                     }
6391                 return true;
6392                 }
6393             case DEL_DIR:
6394                 {
6395                 status("          : %s", dirName.c_str());
6396                 String fullDir = parent.resolve(dirName);
6397                 if (!removeDirectory(fullDir))
6398                     return false;
6399                 return true;
6400                 }
6401             }
6402         return true;
6403         }
6405     virtual bool parse(Element *elem)
6406         {
6407         if (!parent.getAttribute(elem, "file", fileName))
6408             return false;
6409         if (fileName.size() > 0)
6410             delType = DEL_FILE;
6411         if (!parent.getAttribute(elem, "dir", dirName))
6412             return false;
6413         if (dirName.size() > 0)
6414             delType = DEL_DIR;
6415         if (fileName.size()>0 && dirName.size()>0)
6416             {
6417             error("<delete> can have one attribute of file= or dir=");
6418             return false;
6419             }
6420         if (fileName.size()==0 && dirName.size()==0)
6421             {
6422             error("<delete> must have one attribute of file= or dir=");
6423             return false;
6424             }
6425         String ret;
6426         if (!parent.getAttribute(elem, "verbose", ret))
6427             return false;
6428         if (ret.size()>0 && !getBool(ret, verbose))
6429             return false;
6430         if (!parent.getAttribute(elem, "quiet", ret))
6431             return false;
6432         if (ret.size()>0 && !getBool(ret, quiet))
6433             return false;
6434         if (!parent.getAttribute(elem, "failonerror", ret))
6435             return false;
6436         if (ret.size()>0 && !getBool(ret, failOnError))
6437             return false;
6438         return true;
6439         }
6441 private:
6443     int delType;
6444     String dirName;
6445     String fileName;
6446     bool verbose;
6447     bool quiet;
6448     bool failOnError;
6449 };
6452 /**
6453  *
6454  */
6455 class TaskJar : public Task
6457 public:
6459     TaskJar(MakeBase &par) : Task(par)
6460         { type = TASK_JAR; name = "jar"; }
6462     virtual ~TaskJar()
6463         {}
6465     virtual bool execute()
6466         {
6467         return true;
6468         }
6470     virtual bool parse(Element *elem)
6471         {
6472         return true;
6473         }
6474 };
6477 /**
6478  *
6479  */
6480 class TaskJavac : public Task
6482 public:
6484     TaskJavac(MakeBase &par) : Task(par)
6485         { type = TASK_JAVAC; name = "javac"; }
6487     virtual ~TaskJavac()
6488         {}
6490     virtual bool execute()
6491         {
6492         return true;
6493         }
6495     virtual bool parse(Element *elem)
6496         {
6497         return true;
6498         }
6499 };
6502 /**
6503  *
6504  */
6505 class TaskLink : public Task
6507 public:
6509     TaskLink(MakeBase &par) : Task(par)
6510         {
6511         type = TASK_LINK; name = "link";
6512         command = "g++";
6513         doStrip = false;
6514                 stripCommand = "strip";
6515                 objcopyCommand = "objcopy";
6516         }
6518     virtual ~TaskLink()
6519         {}
6521     virtual bool execute()
6522         {
6523         if (!listFiles(parent, fileSet))
6524             return false;
6525         String fileSetDir = fileSet.getDirectory();
6526         //trace("%d files in %s", fileSet.size(), fileSetDir.c_str());
6527         bool doit = false;
6528         String fullTarget = parent.resolve(fileName);
6529         String cmd = command;
6530         cmd.append(" -o ");
6531         cmd.append(fullTarget);
6532         cmd.append(" ");
6533         cmd.append(flags);
6534         for (unsigned int i=0 ; i<fileSet.size() ; i++)
6535             {
6536             cmd.append(" ");
6537             String obj;
6538             if (fileSetDir.size()>0)
6539                 {
6540                 obj.append(fileSetDir);
6541                 obj.append("/");
6542                 }
6543             obj.append(fileSet[i]);
6544             String fullObj = parent.resolve(obj);
6545             cmd.append(fullObj);
6546             //trace("link: tgt:%s obj:%s", fullTarget.c_str(),
6547             //          fullObj.c_str());
6548             if (isNewerThan(fullObj, fullTarget))
6549                 doit = true;
6550             }
6551         cmd.append(" ");
6552         cmd.append(libs);
6553         if (!doit)
6554             {
6555             //trace("link not needed");
6556             return true;
6557             }
6558         //trace("LINK cmd:%s", cmd.c_str());
6561         String outbuf, errbuf;
6562         if (!executeCommand(cmd.c_str(), "", outbuf, errbuf))
6563             {
6564             error("LINK problem: %s", errbuf.c_str());
6565             return false;
6566             }
6568         if (symFileName.size()>0)
6569             {
6570             String symFullName = parent.resolve(symFileName);
6571             cmd = objcopyCommand;
6572             cmd.append(" --only-keep-debug ");
6573             cmd.append(getNativePath(fullTarget));
6574             cmd.append(" ");
6575             cmd.append(getNativePath(symFullName));
6576             if (!executeCommand(cmd, "", outbuf, errbuf))
6577                 {
6578                 error("<strip> symbol file failed : %s", errbuf.c_str());
6579                 return false;
6580                 }
6581             }
6582             
6583         if (doStrip)
6584             {
6585             cmd = stripCommand;
6586             cmd.append(" ");
6587             cmd.append(getNativePath(fullTarget));
6588             if (!executeCommand(cmd, "", outbuf, errbuf))
6589                {
6590                error("<strip> failed : %s", errbuf.c_str());
6591                return false;
6592                }
6593             }
6595         return true;
6596         }
6598     virtual bool parse(Element *elem)
6599         {
6600         String s;
6601         if (!parent.getAttribute(elem, "command", s))
6602             return false;
6603         if (s.size()>0)
6604             command = s;
6605         if (!parent.getAttribute(elem, "objcopycommand", s))
6606             return false;
6607         if (s.size()>0)
6608             objcopyCommand = s;
6609         if (!parent.getAttribute(elem, "stripcommand", s))
6610             return false;
6611         if (s.size()>0)
6612             stripCommand = s;
6613         if (!parent.getAttribute(elem, "out", fileName))
6614             return false;
6615         if (!parent.getAttribute(elem, "strip", s))
6616             return false;
6617         if (s.size()>0 && !getBool(s, doStrip))
6618             return false;
6619         if (!parent.getAttribute(elem, "symfile", symFileName))
6620             return false;
6621             
6622         std::vector<Element *> children = elem->getChildren();
6623         for (unsigned int i=0 ; i<children.size() ; i++)
6624             {
6625             Element *child = children[i];
6626             String tagName = child->getName();
6627             if (tagName == "fileset")
6628                 {
6629                 if (!parseFileSet(child, parent, fileSet))
6630                     return false;
6631                 }
6632             else if (tagName == "flags")
6633                 {
6634                 if (!parent.getValue(child, flags))
6635                     return false;
6636                 flags = strip(flags);
6637                 }
6638             else if (tagName == "libs")
6639                 {
6640                 if (!parent.getValue(child, libs))
6641                     return false;
6642                 libs = strip(libs);
6643                 }
6644             }
6645         return true;
6646         }
6648 private:
6650     String  command;
6651     String  fileName;
6652     String  flags;
6653     String  libs;
6654     FileSet fileSet;
6655     bool    doStrip;
6656     String  symFileName;
6657     String  stripCommand;
6658     String  objcopyCommand;
6660 };
6664 /**
6665  * Create a named directory
6666  */
6667 class TaskMakeFile : public Task
6669 public:
6671     TaskMakeFile(MakeBase &par) : Task(par)
6672         { type = TASK_MAKEFILE; name = "makefile"; }
6674     virtual ~TaskMakeFile()
6675         {}
6677     virtual bool execute()
6678         {
6679         status("          : %s", fileName.c_str());
6680         String fullName = parent.resolve(fileName);
6681         if (!isNewerThan(parent.getURI().getPath(), fullName))
6682             {
6683             //trace("skipped <makefile>");
6684             return true;
6685             }
6686         //trace("fullName:%s", fullName.c_str());
6687         FILE *f = fopen(fullName.c_str(), "w");
6688         if (!f)
6689             {
6690             error("<makefile> could not open %s for writing : %s",
6691                 fullName.c_str(), strerror(errno));
6692             return false;
6693             }
6694         for (unsigned int i=0 ; i<text.size() ; i++)
6695             fputc(text[i], f);
6696         fputc('\n', f);
6697         fclose(f);
6698         return true;
6699         }
6701     virtual bool parse(Element *elem)
6702         {
6703         if (!parent.getAttribute(elem, "file", fileName))
6704             return false;
6705         if (fileName.size() == 0)
6706             {
6707             error("<makefile> requires 'file=\"filename\"' attribute");
6708             return false;
6709             }
6710         if (!parent.getValue(elem, text))
6711             return false;
6712         text = leftJustify(text);
6713         //trace("dirname:%s", dirName.c_str());
6714         return true;
6715         }
6717 private:
6719     String fileName;
6720     String text;
6721 };
6725 /**
6726  * Create a named directory
6727  */
6728 class TaskMkDir : public Task
6730 public:
6732     TaskMkDir(MakeBase &par) : Task(par)
6733         { type = TASK_MKDIR; name = "mkdir"; }
6735     virtual ~TaskMkDir()
6736         {}
6738     virtual bool execute()
6739         {
6740         status("          : %s", dirName.c_str());
6741         String fullDir = parent.resolve(dirName);
6742         //trace("fullDir:%s", fullDir.c_str());
6743         if (!createDirectory(fullDir))
6744             return false;
6745         return true;
6746         }
6748     virtual bool parse(Element *elem)
6749         {
6750         if (!parent.getAttribute(elem, "dir", dirName))
6751             return false;
6752         if (dirName.size() == 0)
6753             {
6754             error("<mkdir> requires 'dir=\"dirname\"' attribute");
6755             return false;
6756             }
6757         return true;
6758         }
6760 private:
6762     String dirName;
6763 };
6767 /**
6768  * Create a named directory
6769  */
6770 class TaskMsgFmt: public Task
6772 public:
6774     TaskMsgFmt(MakeBase &par) : Task(par)
6775          {
6776          type    = TASK_MSGFMT;
6777          name    = "msgfmt";
6778          command = "msgfmt";
6779          owndir  = false;
6780          outName = "";
6781          }
6783     virtual ~TaskMsgFmt()
6784         {}
6786     virtual bool execute()
6787         {
6788         if (!listFiles(parent, fileSet))
6789             return false;
6790         String fileSetDir = fileSet.getDirectory();
6792         //trace("msgfmt: %d", fileSet.size());
6793         for (unsigned int i=0 ; i<fileSet.size() ; i++)
6794             {
6795             String fileName = fileSet[i];
6796             if (getSuffix(fileName) != "po")
6797                 continue;
6798             String sourcePath;
6799             if (fileSetDir.size()>0)
6800                 {
6801                 sourcePath.append(fileSetDir);
6802                 sourcePath.append("/");
6803                 }
6804             sourcePath.append(fileName);
6805             String fullSource = parent.resolve(sourcePath);
6807             String destPath;
6808             if (toDirName.size()>0)
6809                 {
6810                 destPath.append(toDirName);
6811                 destPath.append("/");
6812                 }
6813             if (owndir)
6814                 {
6815                 String subdir = fileName;
6816                 unsigned int pos = subdir.find_last_of('.');
6817                 if (pos != subdir.npos)
6818                     subdir = subdir.substr(0, pos);
6819                 destPath.append(subdir);
6820                 destPath.append("/");
6821                 }
6822             //Pick the output file name
6823             if (outName.size() > 0)
6824                 {
6825                 destPath.append(outName);
6826                 }
6827             else
6828                 {
6829                 destPath.append(fileName);
6830                 destPath[destPath.size()-2] = 'm';
6831                 }
6833             String fullDest = parent.resolve(destPath);
6835             if (!isNewerThan(fullSource, fullDest))
6836                 {
6837                 //trace("skip %s", fullSource.c_str());
6838                 continue;
6839                 }
6840                 
6841             String cmd = command;
6842             cmd.append(" ");
6843             cmd.append(fullSource);
6844             cmd.append(" -o ");
6845             cmd.append(fullDest);
6846             
6847             int pos = fullDest.find_last_of('/');
6848             if (pos>0)
6849                 {
6850                 String fullDestPath = fullDest.substr(0, pos);
6851                 if (!createDirectory(fullDestPath))
6852                     return false;
6853                 }
6857             String outString, errString;
6858             if (!executeCommand(cmd.c_str(), "", outString, errString))
6859                 {
6860                 error("<msgfmt> problem: %s", errString.c_str());
6861                 return false;
6862                 }
6863             }
6865         return true;
6866         }
6868     virtual bool parse(Element *elem)
6869         {
6870         String s;
6871         if (!parent.getAttribute(elem, "command", s))
6872             return false;
6873         if (s.size()>0)
6874             command = s;
6875         if (!parent.getAttribute(elem, "todir", toDirName))
6876             return false;
6877         if (!parent.getAttribute(elem, "out", outName))
6878             return false;
6879         if (!parent.getAttribute(elem, "owndir", s))
6880             return false;
6881         if (s.size()>0 && !getBool(s, owndir))
6882             return false;
6883             
6884         std::vector<Element *> children = elem->getChildren();
6885         for (unsigned int i=0 ; i<children.size() ; i++)
6886             {
6887             Element *child = children[i];
6888             String tagName = child->getName();
6889             if (tagName == "fileset")
6890                 {
6891                 if (!parseFileSet(child, parent, fileSet))
6892                     return false;
6893                 }
6894             }
6895         return true;
6896         }
6898 private:
6900     String  command;
6901     String  toDirName;
6902     String  outName;
6903     FileSet fileSet;
6904     bool    owndir;
6906 };
6912 /**
6913  *  Process an archive to allow random access
6914  */
6915 class TaskRanlib : public Task
6917 public:
6919     TaskRanlib(MakeBase &par) : Task(par)
6920         {
6921         type = TASK_RANLIB; name = "ranlib";
6922         command = "ranlib";
6923         }
6925     virtual ~TaskRanlib()
6926         {}
6928     virtual bool execute()
6929         {
6930         String fullName = parent.resolve(fileName);
6931         //trace("fullDir:%s", fullDir.c_str());
6932         String cmd = command;
6933         cmd.append(" ");
6934         cmd.append(fullName);
6935         String outbuf, errbuf;
6936         if (!executeCommand(cmd, "", outbuf, errbuf))
6937             return false;
6938         return true;
6939         }
6941     virtual bool parse(Element *elem)
6942         {
6943         String s;
6944         if (!parent.getAttribute(elem, "command", s))
6945             return false;
6946         if (s.size()>0)
6947            command = s;
6948         if (!parent.getAttribute(elem, "file", fileName))
6949             return false;
6950         if (fileName.size() == 0)
6951             {
6952             error("<ranlib> requires 'file=\"fileNname\"' attribute");
6953             return false;
6954             }
6955         return true;
6956         }
6958 private:
6960     String fileName;
6961     String command;
6962 };
6966 /**
6967  * Run the "ar" command to archive .o's into a .a
6968  */
6969 class TaskRC : public Task
6971 public:
6973     TaskRC(MakeBase &par) : Task(par)
6974         {
6975         type = TASK_RC; name = "rc";
6976         command = "windres";
6977         }
6979     virtual ~TaskRC()
6980         {}
6982     virtual bool execute()
6983         {
6984         String fullFile = parent.resolve(fileName);
6985         String fullOut  = parent.resolve(outName);
6986         if (!isNewerThan(fullFile, fullOut))
6987             return true;
6988         String cmd = command;
6989         cmd.append(" -o ");
6990         cmd.append(fullOut);
6991         cmd.append(" ");
6992         cmd.append(flags);
6993         cmd.append(" ");
6994         cmd.append(fullFile);
6996         String outString, errString;
6997         if (!executeCommand(cmd.c_str(), "", outString, errString))
6998             {
6999             error("RC problem: %s", errString.c_str());
7000             return false;
7001             }
7002         return true;
7003         }
7005     virtual bool parse(Element *elem)
7006         {
7007         if (!parent.getAttribute(elem, "command", command))
7008             return false;
7009         if (!parent.getAttribute(elem, "file", fileName))
7010             return false;
7011         if (!parent.getAttribute(elem, "out", outName))
7012             return false;
7013         std::vector<Element *> children = elem->getChildren();
7014         for (unsigned int i=0 ; i<children.size() ; i++)
7015             {
7016             Element *child = children[i];
7017             String tagName = child->getName();
7018             if (tagName == "flags")
7019                 {
7020                 if (!parent.getValue(child, flags))
7021                     return false;
7022                 }
7023             }
7024         return true;
7025         }
7027 private:
7029     String command;
7030     String flags;
7031     String fileName;
7032     String outName;
7034 };
7038 /**
7039  *  Collect .o's into a .so or DLL
7040  */
7041 class TaskSharedLib : public Task
7043 public:
7045     TaskSharedLib(MakeBase &par) : Task(par)
7046         {
7047         type = TASK_SHAREDLIB; name = "dll";
7048         command = "dllwrap";
7049         }
7051     virtual ~TaskSharedLib()
7052         {}
7054     virtual bool execute()
7055         {
7056         //trace("###########HERE %d", fileSet.size());
7057         bool doit = false;
7058         
7059         String fullOut = parent.resolve(fileName);
7060         //trace("ar fullout: %s", fullOut.c_str());
7061         
7062         if (!listFiles(parent, fileSet))
7063             return false;
7064         String fileSetDir = fileSet.getDirectory();
7066         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7067             {
7068             String fname;
7069             if (fileSetDir.size()>0)
7070                 {
7071                 fname.append(fileSetDir);
7072                 fname.append("/");
7073                 }
7074             fname.append(fileSet[i]);
7075             String fullName = parent.resolve(fname);
7076             //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
7077             if (isNewerThan(fullName, fullOut))
7078                 doit = true;
7079             }
7080         //trace("Needs it:%d", doit);
7081         if (!doit)
7082             {
7083             return true;
7084             }
7086         String cmd = "dllwrap";
7087         cmd.append(" -o ");
7088         cmd.append(fullOut);
7089         if (defFileName.size()>0)
7090             {
7091             cmd.append(" --def ");
7092             cmd.append(defFileName);
7093             cmd.append(" ");
7094             }
7095         if (impFileName.size()>0)
7096             {
7097             cmd.append(" --implib ");
7098             cmd.append(impFileName);
7099             cmd.append(" ");
7100             }
7101         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7102             {
7103             String fname;
7104             if (fileSetDir.size()>0)
7105                 {
7106                 fname.append(fileSetDir);
7107                 fname.append("/");
7108                 }
7109             fname.append(fileSet[i]);
7110             String fullName = parent.resolve(fname);
7112             cmd.append(" ");
7113             cmd.append(fullName);
7114             }
7115         cmd.append(" ");
7116         cmd.append(libs);
7118         String outString, errString;
7119         if (!executeCommand(cmd.c_str(), "", outString, errString))
7120             {
7121             error("<sharedlib> problem: %s", errString.c_str());
7122             return false;
7123             }
7125         return true;
7126         }
7128     virtual bool parse(Element *elem)
7129         {
7130         if (!parent.getAttribute(elem, "file", fileName))
7131             return false;
7132         if (!parent.getAttribute(elem, "import", impFileName))
7133             return false;
7134         if (!parent.getAttribute(elem, "def", defFileName))
7135             return false;
7136             
7137         std::vector<Element *> children = elem->getChildren();
7138         for (unsigned int i=0 ; i<children.size() ; i++)
7139             {
7140             Element *child = children[i];
7141             String tagName = child->getName();
7142             if (tagName == "fileset")
7143                 {
7144                 if (!parseFileSet(child, parent, fileSet))
7145                     return false;
7146                 }
7147             else if (tagName == "libs")
7148                 {
7149                 if (!parent.getValue(child, libs))
7150                     return false;
7151                 libs = strip(libs);
7152                 }
7153             }
7154         return true;
7155         }
7157 private:
7159     String command;
7160     String fileName;
7161     String defFileName;
7162     String impFileName;
7163     FileSet fileSet;
7164     String libs;
7166 };
7170 /**
7171  * Run the "ar" command to archive .o's into a .a
7172  */
7173 class TaskStaticLib : public Task
7175 public:
7177     TaskStaticLib(MakeBase &par) : Task(par)
7178         {
7179         type = TASK_STATICLIB; name = "staticlib";
7180         command = "ar crv";
7181         }
7183     virtual ~TaskStaticLib()
7184         {}
7186     virtual bool execute()
7187         {
7188         //trace("###########HERE %d", fileSet.size());
7189         bool doit = false;
7190         
7191         String fullOut = parent.resolve(fileName);
7192         //trace("ar fullout: %s", fullOut.c_str());
7193         
7194         if (!listFiles(parent, fileSet))
7195             return false;
7196         String fileSetDir = fileSet.getDirectory();
7198         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7199             {
7200             String fname;
7201             if (fileSetDir.size()>0)
7202                 {
7203                 fname.append(fileSetDir);
7204                 fname.append("/");
7205                 }
7206             fname.append(fileSet[i]);
7207             String fullName = parent.resolve(fname);
7208             //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
7209             if (isNewerThan(fullName, fullOut))
7210                 doit = true;
7211             }
7212         //trace("Needs it:%d", doit);
7213         if (!doit)
7214             {
7215             return true;
7216             }
7218         String cmd = command;
7219         cmd.append(" ");
7220         cmd.append(fullOut);
7221         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7222             {
7223             String fname;
7224             if (fileSetDir.size()>0)
7225                 {
7226                 fname.append(fileSetDir);
7227                 fname.append("/");
7228                 }
7229             fname.append(fileSet[i]);
7230             String fullName = parent.resolve(fname);
7232             cmd.append(" ");
7233             cmd.append(fullName);
7234             }
7236         String outString, errString;
7237         if (!executeCommand(cmd.c_str(), "", outString, errString))
7238             {
7239             error("<staticlib> problem: %s", errString.c_str());
7240             return false;
7241             }
7243         return true;
7244         }
7247     virtual bool parse(Element *elem)
7248         {
7249         String s;
7250         if (!parent.getAttribute(elem, "command", s))
7251             return false;
7252         if (s.size()>0)
7253             command = s;
7254         if (!parent.getAttribute(elem, "file", fileName))
7255             return false;
7256             
7257         std::vector<Element *> children = elem->getChildren();
7258         for (unsigned int i=0 ; i<children.size() ; i++)
7259             {
7260             Element *child = children[i];
7261             String tagName = child->getName();
7262             if (tagName == "fileset")
7263                 {
7264                 if (!parseFileSet(child, parent, fileSet))
7265                     return false;
7266                 }
7267             }
7268         return true;
7269         }
7271 private:
7273     String command;
7274     String fileName;
7275     FileSet fileSet;
7277 };
7282 /**
7283  * Strip an executable
7284  */
7285 class TaskStrip : public Task
7287 public:
7289     TaskStrip(MakeBase &par) : Task(par)
7290         { type = TASK_STRIP; name = "strip"; }
7292     virtual ~TaskStrip()
7293         {}
7295     virtual bool execute()
7296         {
7297         String fullName = parent.resolve(fileName);
7298         //trace("fullDir:%s", fullDir.c_str());
7299         String cmd;
7300         String outbuf, errbuf;
7302         if (symFileName.size()>0)
7303             {
7304             String symFullName = parent.resolve(symFileName);
7305             cmd = "objcopy --only-keep-debug ";
7306             cmd.append(getNativePath(fullName));
7307             cmd.append(" ");
7308             cmd.append(getNativePath(symFullName));
7309             if (!executeCommand(cmd, "", outbuf, errbuf))
7310                 {
7311                 error("<strip> symbol file failed : %s", errbuf.c_str());
7312                 return false;
7313                 }
7314             }
7315             
7316         cmd = "strip ";
7317         cmd.append(getNativePath(fullName));
7318         if (!executeCommand(cmd, "", outbuf, errbuf))
7319             {
7320             error("<strip> failed : %s", errbuf.c_str());
7321             return false;
7322             }
7323         return true;
7324         }
7326     virtual bool parse(Element *elem)
7327         {
7328         if (!parent.getAttribute(elem, "file", fileName))
7329             return false;
7330         if (!parent.getAttribute(elem, "symfile", symFileName))
7331             return false;
7332         if (fileName.size() == 0)
7333             {
7334             error("<strip> requires 'file=\"fileName\"' attribute");
7335             return false;
7336             }
7337         return true;
7338         }
7340 private:
7342     String fileName;
7343     String symFileName;
7344 };
7347 /**
7348  *
7349  */
7350 class TaskTouch : public Task
7352 public:
7354     TaskTouch(MakeBase &par) : Task(par)
7355         { type = TASK_TOUCH; name = "touch"; }
7357     virtual ~TaskTouch()
7358         {}
7360     virtual bool execute()
7361         {
7362         String fullName = parent.resolve(fileName);
7363         String nativeFile = getNativePath(fullName);
7364         if (!isRegularFile(fullName) && !isDirectory(fullName))
7365             {            
7366             // S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH
7367             int ret = creat(nativeFile.c_str(), 0666);
7368             if (ret != 0) 
7369                 {
7370                 error("<touch> could not create '%s' : %s",
7371                     nativeFile.c_str(), strerror(ret));
7372                 return false;
7373                 }
7374             return true;
7375             }
7376         int ret = utime(nativeFile.c_str(), (struct utimbuf *)0);
7377         if (ret != 0)
7378             {
7379             error("<touch> could not update the modification time for '%s' : %s",
7380                 nativeFile.c_str(), strerror(ret));
7381             return false;
7382             }
7383         return true;
7384         }
7386     virtual bool parse(Element *elem)
7387         {
7388         //trace("touch parse");
7389         if (!parent.getAttribute(elem, "file", fileName))
7390             return false;
7391         if (fileName.size() == 0)
7392             {
7393             error("<touch> requires 'file=\"fileName\"' attribute");
7394             return false;
7395             }
7396         return true;
7397         }
7399     String fileName;
7400 };
7403 /**
7404  *
7405  */
7406 class TaskTstamp : public Task
7408 public:
7410     TaskTstamp(MakeBase &par) : Task(par)
7411         { type = TASK_TSTAMP; name = "tstamp"; }
7413     virtual ~TaskTstamp()
7414         {}
7416     virtual bool execute()
7417         {
7418         return true;
7419         }
7421     virtual bool parse(Element *elem)
7422         {
7423         //trace("tstamp parse");
7424         return true;
7425         }
7426 };
7430 /**
7431  *
7432  */
7433 Task *Task::createTask(Element *elem, int lineNr)
7435     String tagName = elem->getName();
7436     //trace("task:%s", tagName.c_str());
7437     Task *task = NULL;
7438     if (tagName == "cc")
7439         task = new TaskCC(parent);
7440     else if (tagName == "copy")
7441         task = new TaskCopy(parent);
7442     else if (tagName == "delete")
7443         task = new TaskDelete(parent);
7444     else if (tagName == "jar")
7445         task = new TaskJar(parent);
7446     else if (tagName == "javac")
7447         task = new TaskJavac(parent);
7448     else if (tagName == "link")
7449         task = new TaskLink(parent);
7450     else if (tagName == "makefile")
7451         task = new TaskMakeFile(parent);
7452     else if (tagName == "mkdir")
7453         task = new TaskMkDir(parent);
7454     else if (tagName == "msgfmt")
7455         task = new TaskMsgFmt(parent);
7456     else if (tagName == "ranlib")
7457         task = new TaskRanlib(parent);
7458     else if (tagName == "rc")
7459         task = new TaskRC(parent);
7460     else if (tagName == "sharedlib")
7461         task = new TaskSharedLib(parent);
7462     else if (tagName == "staticlib")
7463         task = new TaskStaticLib(parent);
7464     else if (tagName == "strip")
7465         task = new TaskStrip(parent);
7466     else if (tagName == "touch")
7467         task = new TaskTouch(parent);
7468     else if (tagName == "tstamp")
7469         task = new TaskTstamp(parent);
7470     else
7471         {
7472         error("Unknown task '%s'", tagName.c_str());
7473         return NULL;
7474         }
7476     task->setLine(lineNr);
7478     if (!task->parse(elem))
7479         {
7480         delete task;
7481         return NULL;
7482         }
7483     return task;
7488 //########################################################################
7489 //# T A R G E T
7490 //########################################################################
7492 /**
7493  *
7494  */
7495 class Target : public MakeBase
7498 public:
7500     /**
7501      *
7502      */
7503     Target(Make &par) : parent(par)
7504         { init(); }
7506     /**
7507      *
7508      */
7509     Target(const Target &other) : parent(other.parent)
7510         { init(); assign(other); }
7512     /**
7513      *
7514      */
7515     Target &operator=(const Target &other)
7516         { init(); assign(other); return *this; }
7518     /**
7519      *
7520      */
7521     virtual ~Target()
7522         { cleanup() ; }
7525     /**
7526      *
7527      */
7528     virtual Make &getParent()
7529         { return parent; }
7531     /**
7532      *
7533      */
7534     virtual String getName()
7535         { return name; }
7537     /**
7538      *
7539      */
7540     virtual void setName(const String &val)
7541         { name = val; }
7543     /**
7544      *
7545      */
7546     virtual String getDescription()
7547         { return description; }
7549     /**
7550      *
7551      */
7552     virtual void setDescription(const String &val)
7553         { description = val; }
7555     /**
7556      *
7557      */
7558     virtual void addDependency(const String &val)
7559         { deps.push_back(val); }
7561     /**
7562      *
7563      */
7564     virtual void parseDependencies(const String &val)
7565         { deps = tokenize(val, ", "); }
7567     /**
7568      *
7569      */
7570     virtual std::vector<String> &getDependencies()
7571         { return deps; }
7573     /**
7574      *
7575      */
7576     virtual String getIf()
7577         { return ifVar; }
7579     /**
7580      *
7581      */
7582     virtual void setIf(const String &val)
7583         { ifVar = val; }
7585     /**
7586      *
7587      */
7588     virtual String getUnless()
7589         { return unlessVar; }
7591     /**
7592      *
7593      */
7594     virtual void setUnless(const String &val)
7595         { unlessVar = val; }
7597     /**
7598      *
7599      */
7600     virtual void addTask(Task *val)
7601         { tasks.push_back(val); }
7603     /**
7604      *
7605      */
7606     virtual std::vector<Task *> &getTasks()
7607         { return tasks; }
7609 private:
7611     void init()
7612         {
7613         }
7615     void cleanup()
7616         {
7617         tasks.clear();
7618         }
7620     void assign(const Target &other)
7621         {
7622         //parent      = other.parent;
7623         name        = other.name;
7624         description = other.description;
7625         ifVar       = other.ifVar;
7626         unlessVar   = other.unlessVar;
7627         deps        = other.deps;
7628         tasks       = other.tasks;
7629         }
7631     Make &parent;
7633     String name;
7635     String description;
7637     String ifVar;
7639     String unlessVar;
7641     std::vector<String> deps;
7643     std::vector<Task *> tasks;
7645 };
7654 //########################################################################
7655 //# M A K E
7656 //########################################################################
7659 /**
7660  *
7661  */
7662 class Make : public MakeBase
7665 public:
7667     /**
7668      *
7669      */
7670     Make()
7671         { init(); }
7673     /**
7674      *
7675      */
7676     Make(const Make &other)
7677         { assign(other); }
7679     /**
7680      *
7681      */
7682     Make &operator=(const Make &other)
7683         { assign(other); return *this; }
7685     /**
7686      *
7687      */
7688     virtual ~Make()
7689         { cleanup(); }
7691     /**
7692      *
7693      */
7694     virtual std::map<String, Target> &getTargets()
7695         { return targets; }
7698     /**
7699      *
7700      */
7701     virtual String version()
7702         { return BUILDTOOL_VERSION; }
7704     /**
7705      * Overload a <property>
7706      */
7707     virtual bool specifyProperty(const String &name,
7708                                  const String &value);
7710     /**
7711      *
7712      */
7713     virtual bool run();
7715     /**
7716      *
7717      */
7718     virtual bool run(const String &target);
7722 private:
7724     /**
7725      *
7726      */
7727     void init();
7729     /**
7730      *
7731      */
7732     void cleanup();
7734     /**
7735      *
7736      */
7737     void assign(const Make &other);
7739     /**
7740      *
7741      */
7742     bool executeTask(Task &task);
7745     /**
7746      *
7747      */
7748     bool executeTarget(Target &target,
7749              std::set<String> &targetsCompleted);
7752     /**
7753      *
7754      */
7755     bool execute();
7757     /**
7758      *
7759      */
7760     bool checkTargetDependencies(Target &prop,
7761                     std::vector<String> &depList);
7763     /**
7764      *
7765      */
7766     bool parsePropertyFile(const String &fileName,
7767                            const String &prefix);
7769     /**
7770      *
7771      */
7772     bool parseProperty(Element *elem);
7774     /**
7775      *
7776      */
7777     bool parseFile();
7779     /**
7780      *
7781      */
7782     std::vector<String> glob(const String &pattern);
7785     //###############
7786     //# Fields
7787     //###############
7789     String projectName;
7791     String currentTarget;
7793     String defaultTarget;
7795     String specifiedTarget;
7797     String baseDir;
7799     String description;
7800     
7801     String envAlias;
7803     //std::vector<Property> properties;
7804     
7805     std::map<String, Target> targets;
7807     std::vector<Task *> allTasks;
7808     
7809     std::map<String, String> specifiedProperties;
7811 };
7814 //########################################################################
7815 //# C L A S S  M A I N T E N A N C E
7816 //########################################################################
7818 /**
7819  *
7820  */
7821 void Make::init()
7823     uri             = "build.xml";
7824     projectName     = "";
7825     currentTarget   = "";
7826     defaultTarget   = "";
7827     specifiedTarget = "";
7828     baseDir         = "";
7829     description     = "";
7830     envAlias        = "";
7831     properties.clear();
7832     for (unsigned int i = 0 ; i < allTasks.size() ; i++)
7833         delete allTasks[i];
7834     allTasks.clear();
7839 /**
7840  *
7841  */
7842 void Make::cleanup()
7844     for (unsigned int i = 0 ; i < allTasks.size() ; i++)
7845         delete allTasks[i];
7846     allTasks.clear();
7851 /**
7852  *
7853  */
7854 void Make::assign(const Make &other)
7856     uri              = other.uri;
7857     projectName      = other.projectName;
7858     currentTarget    = other.currentTarget;
7859     defaultTarget    = other.defaultTarget;
7860     specifiedTarget  = other.specifiedTarget;
7861     baseDir          = other.baseDir;
7862     description      = other.description;
7863     properties       = other.properties;
7868 //########################################################################
7869 //# U T I L I T Y    T A S K S
7870 //########################################################################
7872 /**
7873  *  Perform a file globbing
7874  */
7875 std::vector<String> Make::glob(const String &pattern)
7877     std::vector<String> res;
7878     return res;
7882 //########################################################################
7883 //# P U B L I C    A P I
7884 //########################################################################
7888 /**
7889  *
7890  */
7891 bool Make::executeTarget(Target &target,
7892              std::set<String> &targetsCompleted)
7895     String name = target.getName();
7897     //First get any dependencies for this target
7898     std::vector<String> deps = target.getDependencies();
7899     for (unsigned int i=0 ; i<deps.size() ; i++)
7900         {
7901         String dep = deps[i];
7902         //Did we do it already?  Skip
7903         if (targetsCompleted.find(dep)!=targetsCompleted.end())
7904             continue;
7905             
7906         std::map<String, Target> &tgts =
7907                target.getParent().getTargets();
7908         std::map<String, Target>::iterator iter =
7909                tgts.find(dep);
7910         if (iter == tgts.end())
7911             {
7912             error("Target '%s' dependency '%s' not found",
7913                       name.c_str(),  dep.c_str());
7914             return false;
7915             }
7916         Target depTarget = iter->second;
7917         if (!executeTarget(depTarget, targetsCompleted))
7918             {
7919             return false;
7920             }
7921         }
7923     status("## Target : %s", name.c_str());
7925     //Now let's do the tasks
7926     std::vector<Task *> &tasks = target.getTasks();
7927     for (unsigned int i=0 ; i<tasks.size() ; i++)
7928         {
7929         Task *task = tasks[i];
7930         status("---- task : %s", task->getName().c_str());
7931         if (!task->execute())
7932             {
7933             return false;
7934             }
7935         }
7936         
7937     targetsCompleted.insert(name);
7938     
7939     return true;
7944 /**
7945  *  Main execute() method.  Start here and work
7946  *  up the dependency tree 
7947  */
7948 bool Make::execute()
7950     status("######## EXECUTE");
7952     //Determine initial target
7953     if (specifiedTarget.size()>0)
7954         {
7955         currentTarget = specifiedTarget;
7956         }
7957     else if (defaultTarget.size()>0)
7958         {
7959         currentTarget = defaultTarget;
7960         }
7961     else
7962         {
7963         error("execute: no specified or default target requested");
7964         return false;
7965         }
7967     std::map<String, Target>::iterator iter =
7968                targets.find(currentTarget);
7969     if (iter == targets.end())
7970         {
7971         error("Initial target '%s' not found",
7972                  currentTarget.c_str());
7973         return false;
7974         }
7975         
7976     //Now run
7977     Target target = iter->second;
7978     std::set<String> targetsCompleted;
7979     if (!executeTarget(target, targetsCompleted))
7980         {
7981         return false;
7982         }
7984     status("######## EXECUTE COMPLETE");
7985     return true;
7991 /**
7992  *
7993  */
7994 bool Make::checkTargetDependencies(Target &target, 
7995                             std::vector<String> &depList)
7997     String tgtName = target.getName().c_str();
7998     depList.push_back(tgtName);
8000     std::vector<String> deps = target.getDependencies();
8001     for (unsigned int i=0 ; i<deps.size() ; i++)
8002         {
8003         String dep = deps[i];
8004         //First thing entered was the starting Target
8005         if (dep == depList[0])
8006             {
8007             error("Circular dependency '%s' found at '%s'",
8008                       dep.c_str(), tgtName.c_str());
8009             std::vector<String>::iterator diter;
8010             for (diter=depList.begin() ; diter!=depList.end() ; diter++)
8011                 {
8012                 error("  %s", diter->c_str());
8013                 }
8014             return false;
8015             }
8017         std::map<String, Target> &tgts =
8018                   target.getParent().getTargets();
8019         std::map<String, Target>::iterator titer = tgts.find(dep);
8020         if (titer == tgts.end())
8021             {
8022             error("Target '%s' dependency '%s' not found",
8023                       tgtName.c_str(), dep.c_str());
8024             return false;
8025             }
8026         if (!checkTargetDependencies(titer->second, depList))
8027             {
8028             return false;
8029             }
8030         }
8031     return true;
8038 static int getword(int pos, const String &inbuf, String &result)
8040     int p = pos;
8041     int len = (int)inbuf.size();
8042     String val;
8043     while (p < len)
8044         {
8045         char ch = inbuf[p];
8046         if (!isalnum(ch) && ch!='.' && ch!='_')
8047             break;
8048         val.push_back(ch);
8049         p++;
8050         }
8051     result = val;
8052     return p;
8058 /**
8059  *
8060  */
8061 bool Make::parsePropertyFile(const String &fileName,
8062                              const String &prefix)
8064     FILE *f = fopen(fileName.c_str(), "r");
8065     if (!f)
8066         {
8067         error("could not open property file %s", fileName.c_str());
8068         return false;
8069         }
8070     int linenr = 0;
8071     while (!feof(f))
8072         {
8073         char buf[256];
8074         if (!fgets(buf, 255, f))
8075             break;
8076         linenr++;
8077         String s = buf;
8078         s = trim(s);
8079         int len = s.size();
8080         if (len == 0)
8081             continue;
8082         if (s[0] == '#')
8083             continue;
8084         String key;
8085         String val;
8086         int p = 0;
8087         int p2 = getword(p, s, key);
8088         if (p2 <= p)
8089             {
8090             error("property file %s, line %d: expected keyword",
8091                     fileName.c_str(), linenr);
8092             return false;
8093             }
8094         if (prefix.size() > 0)
8095             {
8096             key.insert(0, prefix);
8097             }
8099         //skip whitespace
8100         for (p=p2 ; p<len ; p++)
8101             if (!isspace(s[p]))
8102                 break;
8104         if (p>=len || s[p]!='=')
8105             {
8106             error("property file %s, line %d: expected '='",
8107                     fileName.c_str(), linenr);
8108             return false;
8109             }
8110         p++;
8112         //skip whitespace
8113         for ( ; p<len ; p++)
8114             if (!isspace(s[p]))
8115                 break;
8117         /* This way expects a word after the =
8118         p2 = getword(p, s, val);
8119         if (p2 <= p)
8120             {
8121             error("property file %s, line %d: expected value",
8122                     fileName.c_str(), linenr);
8123             return false;
8124             }
8125         */
8126         // This way gets the rest of the line after the =
8127         if (p>=len)
8128             {
8129             error("property file %s, line %d: expected value",
8130                     fileName.c_str(), linenr);
8131             return false;
8132             }
8133         val = s.substr(p);
8134         if (key.size()==0)
8135             continue;
8136         //allow property to be set, even if val=""
8138         //trace("key:'%s' val:'%s'", key.c_str(), val.c_str());
8139         //See if we wanted to overload this property
8140         std::map<String, String>::iterator iter =
8141             specifiedProperties.find(key);
8142         if (iter!=specifiedProperties.end())
8143             {
8144             val = iter->second;
8145             status("overloading property '%s' = '%s'",
8146                    key.c_str(), val.c_str());
8147             }
8148         properties[key] = val;
8149         }
8150     fclose(f);
8151     return true;
8157 /**
8158  *
8159  */
8160 bool Make::parseProperty(Element *elem)
8162     std::vector<Attribute> &attrs = elem->getAttributes();
8163     for (unsigned int i=0 ; i<attrs.size() ; i++)
8164         {
8165         String attrName = attrs[i].getName();
8166         String attrVal  = attrs[i].getValue();
8168         if (attrName == "name")
8169             {
8170             String val;
8171             if (!getAttribute(elem, "value", val))
8172                 return false;
8173             if (val.size() > 0)
8174                 {
8175                 properties[attrVal] = val;
8176                 }
8177             else
8178                 {
8179                 if (!getAttribute(elem, "location", val))
8180                     return false;
8181                 //let the property exist, even if not defined
8182                 properties[attrVal] = val;
8183                 }
8184             //See if we wanted to overload this property
8185             std::map<String, String>::iterator iter =
8186                 specifiedProperties.find(attrVal);
8187             if (iter != specifiedProperties.end())
8188                 {
8189                 val = iter->second;
8190                 status("overloading property '%s' = '%s'",
8191                     attrVal.c_str(), val.c_str());
8192                 properties[attrVal] = val;
8193                 }
8194             }
8195         else if (attrName == "file")
8196             {
8197             String prefix;
8198             if (!getAttribute(elem, "prefix", prefix))
8199                 return false;
8200             if (prefix.size() > 0)
8201                 {
8202                 if (prefix[prefix.size()-1] != '.')
8203                     prefix.push_back('.');
8204                 }
8205             if (!parsePropertyFile(attrName, prefix))
8206                 return false;
8207             }
8208         else if (attrName == "environment")
8209             {
8210             if (envAlias.size() > 0)
8211                 {
8212                 error("environment property can only be set once");
8213                 return false;
8214                 }
8215             envAlias = attrVal;
8216             }
8217         }
8219     return true;
8225 /**
8226  *
8227  */
8228 bool Make::parseFile()
8230     status("######## PARSE : %s", uri.getPath().c_str());
8232     setLine(0);
8234     Parser parser;
8235     Element *root = parser.parseFile(uri.getNativePath());
8236     if (!root)
8237         {
8238         error("Could not open %s for reading",
8239               uri.getNativePath().c_str());
8240         return false;
8241         }
8242     
8243     setLine(root->getLine());
8245     if (root->getChildren().size()==0 ||
8246         root->getChildren()[0]->getName()!="project")
8247         {
8248         error("Main xml element should be <project>");
8249         delete root;
8250         return false;
8251         }
8253     //########## Project attributes
8254     Element *project = root->getChildren()[0];
8255     String s = project->getAttribute("name");
8256     if (s.size() > 0)
8257         projectName = s;
8258     s = project->getAttribute("default");
8259     if (s.size() > 0)
8260         defaultTarget = s;
8261     s = project->getAttribute("basedir");
8262     if (s.size() > 0)
8263         baseDir = s;
8265     //######### PARSE MEMBERS
8266     std::vector<Element *> children = project->getChildren();
8267     for (unsigned int i=0 ; i<children.size() ; i++)
8268         {
8269         Element *elem = children[i];
8270         setLine(elem->getLine());
8271         String tagName = elem->getName();
8273         //########## DESCRIPTION
8274         if (tagName == "description")
8275             {
8276             description = parser.trim(elem->getValue());
8277             }
8279         //######### PROPERTY
8280         else if (tagName == "property")
8281             {
8282             if (!parseProperty(elem))
8283                 return false;
8284             }
8286         //######### TARGET
8287         else if (tagName == "target")
8288             {
8289             String tname   = elem->getAttribute("name");
8290             String tdesc   = elem->getAttribute("description");
8291             String tdeps   = elem->getAttribute("depends");
8292             String tif     = elem->getAttribute("if");
8293             String tunless = elem->getAttribute("unless");
8294             Target target(*this);
8295             target.setName(tname);
8296             target.setDescription(tdesc);
8297             target.parseDependencies(tdeps);
8298             target.setIf(tif);
8299             target.setUnless(tunless);
8300             std::vector<Element *> telems = elem->getChildren();
8301             for (unsigned int i=0 ; i<telems.size() ; i++)
8302                 {
8303                 Element *telem = telems[i];
8304                 Task breeder(*this);
8305                 Task *task = breeder.createTask(telem, telem->getLine());
8306                 if (!task)
8307                     return false;
8308                 allTasks.push_back(task);
8309                 target.addTask(task);
8310                 }
8312             //Check name
8313             if (tname.size() == 0)
8314                 {
8315                 error("no name for target");
8316                 return false;
8317                 }
8318             //Check for duplicate name
8319             if (targets.find(tname) != targets.end())
8320                 {
8321                 error("target '%s' already defined", tname.c_str());
8322                 return false;
8323                 }
8324             //more work than targets[tname]=target, but avoids default allocator
8325             targets.insert(std::make_pair<String, Target>(tname, target));
8326             }
8327         //######### none of the above
8328         else
8329             {
8330             error("unknown toplevel tag: <%s>", tagName.c_str());
8331             return false;
8332             }
8334         }
8336     std::map<String, Target>::iterator iter;
8337     for (iter = targets.begin() ; iter!= targets.end() ; iter++)
8338         {
8339         Target tgt = iter->second;
8340         std::vector<String> depList;
8341         if (!checkTargetDependencies(tgt, depList))
8342             {
8343             return false;
8344             }
8345         }
8348     delete root;
8349     status("######## PARSE COMPLETE");
8350     return true;
8354 /**
8355  * Overload a <property>
8356  */
8357 bool Make::specifyProperty(const String &name, const String &value)
8359     if (specifiedProperties.find(name) != specifiedProperties.end())
8360         {
8361         error("Property %s already specified", name.c_str());
8362         return false;
8363         }
8364     specifiedProperties[name] = value;
8365     return true;
8370 /**
8371  *
8372  */
8373 bool Make::run()
8375     if (!parseFile())
8376         return false;
8377         
8378     if (!execute())
8379         return false;
8381     return true;
8387 /**
8388  * Get a formatted MM:SS.sss time elapsed string
8389  */ 
8390 static String
8391 timeDiffString(struct timeval &x, struct timeval &y)
8393     long microsX  = x.tv_usec;
8394     long secondsX = x.tv_sec;
8395     long microsY  = y.tv_usec;
8396     long secondsY = y.tv_sec;
8397     if (microsX < microsY)
8398         {
8399         microsX += 1000000;
8400         secondsX -= 1;
8401         }
8403     int seconds = (int)(secondsX - secondsY);
8404     int millis  = (int)((microsX - microsY)/1000);
8406     int minutes = seconds/60;
8407     seconds -= minutes*60;
8408     char buf[80];
8409     snprintf(buf, 79, "%dm %d.%03ds", minutes, seconds, millis);
8410     String ret = buf;
8411     return ret;
8412     
8415 /**
8416  *
8417  */
8418 bool Make::run(const String &target)
8420     status("####################################################");
8421     status("#   %s", version().c_str());
8422     status("####################################################");
8423     struct timeval timeStart, timeEnd;
8424     ::gettimeofday(&timeStart, NULL);
8425     specifiedTarget = target;
8426     if (!run())
8427         return false;
8428     ::gettimeofday(&timeEnd, NULL);
8429     String timeStr = timeDiffString(timeEnd, timeStart);
8430     status("####################################################");
8431     status("#   BuildTool Completed : %s", timeStr.c_str());
8432     status("####################################################");
8433     return true;
8442 }// namespace buildtool
8443 //########################################################################
8444 //# M A I N
8445 //########################################################################
8447 typedef buildtool::String String;
8449 /**
8450  *  Format an error message in printf() style
8451  */
8452 static void error(char *fmt, ...)
8454     va_list ap;
8455     va_start(ap, fmt);
8456     fprintf(stderr, "BuildTool error: ");
8457     vfprintf(stderr, fmt, ap);
8458     fprintf(stderr, "\n");
8459     va_end(ap);
8463 static bool parseProperty(const String &s, String &name, String &val)
8465     int len = s.size();
8466     int i;
8467     for (i=0 ; i<len ; i++)
8468         {
8469         char ch = s[i];
8470         if (ch == '=')
8471             break;
8472         name.push_back(ch);
8473         }
8474     if (i>=len || s[i]!='=')
8475         {
8476         error("property requires -Dname=value");
8477         return false;
8478         }
8479     i++;
8480     for ( ; i<len ; i++)
8481         {
8482         char ch = s[i];
8483         val.push_back(ch);
8484         }
8485     return true;
8489 /**
8490  * Compare a buffer with a key, for the length of the key
8491  */
8492 static bool sequ(const String &buf, char *key)
8494     int len = buf.size();
8495     for (int i=0 ; key[i] && i<len ; i++)
8496         {
8497         if (key[i] != buf[i])
8498             return false;
8499         }        
8500     return true;
8503 static void usage(int argc, char **argv)
8505     printf("usage:\n");
8506     printf("   %s [options] [target]\n", argv[0]);
8507     printf("Options:\n");
8508     printf("  -help, -h              print this message\n");
8509     printf("  -version               print the version information and exit\n");
8510     printf("  -file <file>           use given buildfile\n");
8511     printf("  -f <file>                 ''\n");
8512     printf("  -D<property>=<value>   use value for given property\n");
8518 /**
8519  * Parse the command-line args, get our options,
8520  * and run this thing
8521  */   
8522 static bool parseOptions(int argc, char **argv)
8524     if (argc < 1)
8525         {
8526         error("Cannot parse arguments");
8527         return false;
8528         }
8530     buildtool::Make make;
8532     String target;
8534     //char *progName = argv[0];
8535     for (int i=1 ; i<argc ; i++)
8536         {
8537         String arg = argv[i];
8538         if (arg.size()>1 && arg[0]=='-')
8539             {
8540             if (arg == "-h" || arg == "-help")
8541                 {
8542                 usage(argc,argv);
8543                 return true;
8544                 }
8545             else if (arg == "-version")
8546                 {
8547                 printf("%s", make.version().c_str());
8548                 return true;
8549                 }
8550             else if (arg == "-f" || arg == "-file")
8551                 {
8552                 if (i>=argc)
8553                    {
8554                    usage(argc, argv);
8555                    return false;
8556                    }
8557                 i++; //eat option
8558                 make.setURI(argv[i]);
8559                 }
8560             else if (arg.size()>2 && sequ(arg, "-D"))
8561                 {
8562                 String s = arg.substr(2, s.size());
8563                 String name, value;
8564                 if (!parseProperty(s, name, value))
8565                    {
8566                    usage(argc, argv);
8567                    return false;
8568                    }
8569                 if (!make.specifyProperty(name, value))
8570                     return false;
8571                 }
8572             else
8573                 {
8574                 error("Unknown option:%s", arg.c_str());
8575                 return false;
8576                 }
8577             }
8578         else
8579             {
8580             if (target.size()>0)
8581                 {
8582                 error("only one initial target");
8583                 usage(argc, argv);
8584                 return false;
8585                 }
8586             target = arg;
8587             }
8588         }
8590     //We have the options.  Now execute them
8591     if (!make.run(target))
8592         return false;
8594     return true;
8600 /*
8601 static bool runMake()
8603     buildtool::Make make;
8604     if (!make.run())
8605         return false;
8606     return true;
8610 static bool pkgConfigTest()
8612     buildtool::PkgConfig pkgConfig;
8613     if (!pkgConfig.readFile("gtk+-2.0.pc"))
8614         return false;
8615     return true;
8620 static bool depTest()
8622     buildtool::DepTool deptool;
8623     deptool.setSourceDirectory("/dev/ink/inkscape/src");
8624     if (!deptool.generateDependencies("build.dep"))
8625         return false;
8626     std::vector<buildtool::DepRec> res =
8627            deptool.loadDepFile("build.dep");
8628     if (res.size() == 0)
8629         return false;
8630     return true;
8633 static bool popenTest()
8635     buildtool::Make make;
8636     buildtool::String out, err;
8637     bool ret = make.executeCommand("gcc xx.cpp", "", out, err);
8638     printf("Popen test:%d '%s' '%s'\n", ret, out.c_str(), err.c_str());
8639     return true;
8643 static bool propFileTest()
8645     buildtool::Make make;
8646     make.parsePropertyFile("test.prop", "test.");
8647     return true;
8649 */
8651 int main(int argc, char **argv)
8654     if (!parseOptions(argc, argv))
8655         return 1;
8656     /*
8657     if (!popenTest())
8658         return 1;
8660     if (!depTest())
8661         return 1;
8662     if (!propFileTest())
8663         return 1;
8664     if (runMake())
8665         return 1;
8666     */
8667     return 0;
8671 //########################################################################
8672 //# E N D 
8673 //########################################################################