Code

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