Code

fix time
[inkscape.git] / buildtool.cpp
1 /**
2  * Simple build automation tool.
3  *
4  * Authors:
5  *   Bob Jamison
6  *
7  * Copyright (C) 2006 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  */  
37 #include <stdio.h>
38 #include <unistd.h>
39 #include <stdarg.h>
40 #include <sys/stat.h>
41 #include <time.h>
42 #include <sys/time.h>
43 #include <dirent.h>
45 #include <string>
46 #include <map>
47 #include <set>
48 #include <vector>
50 #ifdef __WIN32__
51 #include <windows.h>
52 #endif
55 #include <errno.h>
60 namespace buildtool
61 {
65 //########################################################################
66 //########################################################################
67 //##  U T I L
68 //########################################################################
69 //########################################################################
71 #ifdef __WIN32__
72 #include <sys/timeb.h>
73 struct timezone {
74       int tz_minuteswest; /* minutes west of Greenwich */
75       int tz_dsttime;     /* type of dst correction */
76     };
78 static int gettimeofday (struct timeval *tv, struct timezone *tz)
79 {
80    struct _timeb tb;
82    if (!tv)
83       return (-1);
85     _ftime (&tb);
86     tv->tv_sec  = tb.time;
87     tv->tv_usec = tb.millitm * 1000 + 500;
88     if (tz)
89         {
90         tz->tz_minuteswest = -60 * _timezone;
91         tz->tz_dsttime = _daylight;
92         }
93     return 0;
94 }
95 #endif
97 //########################################################################
98 //########################################################################
99 //##  R E G E X P
100 //########################################################################
101 //########################################################################
103 /**
104  * This is the T-Rex regular expression library, which we
105  * gratefully acknowledge.  It's clean code and small size allow
106  * us to embed it in BuildTool without adding a dependency
107  *
108  */    
110 //begin trex.h
112 #ifndef _TREX_H_
113 #define _TREX_H_
114 /***************************************************************
115     T-Rex a tiny regular expression library
117     Copyright (C) 2003-2006 Alberto Demichelis
119     This software is provided 'as-is', without any express 
120     or implied warranty. In no event will the authors be held 
121     liable for any damages arising from the use of this software.
123     Permission is granted to anyone to use this software for 
124     any purpose, including commercial applications, and to alter
125     it and redistribute it freely, subject to the following restrictions:
127         1. The origin of this software must not be misrepresented;
128         you must not claim that you wrote the original software.
129         If you use this software in a product, an acknowledgment
130         in the product documentation would be appreciated but
131         is not required.
133         2. Altered source versions must be plainly marked as such,
134         and must not be misrepresented as being the original software.
136         3. This notice may not be removed or altered from any
137         source distribution.
139 ****************************************************************/
141 #ifdef _UNICODE
142 #define TRexChar unsigned short
143 #define MAX_CHAR 0xFFFF
144 #define _TREXC(c) L##c 
145 #define trex_strlen wcslen
146 #define trex_printf wprintf
147 #else
148 #define TRexChar char
149 #define MAX_CHAR 0xFF
150 #define _TREXC(c) (c) 
151 #define trex_strlen strlen
152 #define trex_printf printf
153 #endif
155 #ifndef TREX_API
156 #define TREX_API extern
157 #endif
159 #define TRex_True 1
160 #define TRex_False 0
162 typedef unsigned int TRexBool;
163 typedef struct TRex TRex;
165 typedef struct {
166     const TRexChar *begin;
167     int len;
168 } TRexMatch;
170 TREX_API TRex *trex_compile(const TRexChar *pattern,const TRexChar **error);
171 TREX_API void trex_free(TRex *exp);
172 TREX_API TRexBool trex_match(TRex* exp,const TRexChar* text);
173 TREX_API TRexBool trex_search(TRex* exp,const TRexChar* text, const TRexChar** out_begin, const TRexChar** out_end);
174 TREX_API TRexBool trex_searchrange(TRex* exp,const TRexChar* text_begin,const TRexChar* text_end,const TRexChar** out_begin, const TRexChar** out_end);
175 TREX_API int trex_getsubexpcount(TRex* exp);
176 TREX_API TRexBool trex_getsubexp(TRex* exp, int n, TRexMatch *subexp);
178 #endif
180 //end trex.h
182 //start trex.c
185 #include <stdio.h>
186 #include <string>
188 /* see copyright notice in trex.h */
189 #include <string.h>
190 #include <stdlib.h>
191 #include <ctype.h>
192 #include <setjmp.h>
193 //#include "trex.h"
195 #ifdef _UINCODE
196 #define scisprint iswprint
197 #define scstrlen wcslen
198 #define scprintf wprintf
199 #define _SC(x) L(x)
200 #else
201 #define scisprint isprint
202 #define scstrlen strlen
203 #define scprintf printf
204 #define _SC(x) (x)
205 #endif
207 #ifdef _DEBUG
208 #include <stdio.h>
210 static const TRexChar *g_nnames[] =
212     _SC("NONE"),_SC("OP_GREEDY"),    _SC("OP_OR"),
213     _SC("OP_EXPR"),_SC("OP_NOCAPEXPR"),_SC("OP_DOT"),    _SC("OP_CLASS"),
214     _SC("OP_CCLASS"),_SC("OP_NCLASS"),_SC("OP_RANGE"),_SC("OP_CHAR"),
215     _SC("OP_EOL"),_SC("OP_BOL"),_SC("OP_WB")
216 };
218 #endif
219 #define OP_GREEDY        (MAX_CHAR+1) // * + ? {n}
220 #define OP_OR            (MAX_CHAR+2)
221 #define OP_EXPR            (MAX_CHAR+3) //parentesis ()
222 #define OP_NOCAPEXPR    (MAX_CHAR+4) //parentesis (?:)
223 #define OP_DOT            (MAX_CHAR+5)
224 #define OP_CLASS        (MAX_CHAR+6)
225 #define OP_CCLASS        (MAX_CHAR+7)
226 #define OP_NCLASS        (MAX_CHAR+8) //negates class the [^
227 #define OP_RANGE        (MAX_CHAR+9)
228 #define OP_CHAR            (MAX_CHAR+10)
229 #define OP_EOL            (MAX_CHAR+11)
230 #define OP_BOL            (MAX_CHAR+12)
231 #define OP_WB            (MAX_CHAR+13)
233 #define TREX_SYMBOL_ANY_CHAR ('.')
234 #define TREX_SYMBOL_GREEDY_ONE_OR_MORE ('+')
235 #define TREX_SYMBOL_GREEDY_ZERO_OR_MORE ('*')
236 #define TREX_SYMBOL_GREEDY_ZERO_OR_ONE ('?')
237 #define TREX_SYMBOL_BRANCH ('|')
238 #define TREX_SYMBOL_END_OF_STRING ('$')
239 #define TREX_SYMBOL_BEGINNING_OF_STRING ('^')
240 #define TREX_SYMBOL_ESCAPE_CHAR ('\\')
243 typedef int TRexNodeType;
245 typedef struct tagTRexNode{
246     TRexNodeType type;
247     int left;
248     int right;
249     int next;
250 }TRexNode;
252 struct TRex{
253     const TRexChar *_eol;
254     const TRexChar *_bol;
255     const TRexChar *_p;
256     int _first;
257     int _op;
258     TRexNode *_nodes;
259     int _nallocated;
260     int _nsize;
261     int _nsubexpr;
262     TRexMatch *_matches;
263     int _currsubexp;
264     void *_jmpbuf;
265     const TRexChar **_error;
266 };
268 static int trex_list(TRex *exp);
270 static int trex_newnode(TRex *exp, TRexNodeType type)
272     TRexNode n;
273     int newid;
274     n.type = type;
275     n.next = n.right = n.left = -1;
276     if(type == OP_EXPR)
277         n.right = exp->_nsubexpr++;
278     if(exp->_nallocated < (exp->_nsize + 1)) {
279         //int oldsize = exp->_nallocated;
280         exp->_nallocated *= 2;
281         exp->_nodes = (TRexNode *)realloc(exp->_nodes, exp->_nallocated * sizeof(TRexNode));
282     }
283     exp->_nodes[exp->_nsize++] = n;
284     newid = exp->_nsize - 1;
285     return (int)newid;
288 static void trex_error(TRex *exp,const TRexChar *error)
290     if(exp->_error) *exp->_error = error;
291     longjmp(*((jmp_buf*)exp->_jmpbuf),-1);
294 static void trex_expect(TRex *exp, int n){
295     if((*exp->_p) != n) 
296         trex_error(exp, _SC("expected paren"));
297     exp->_p++;
300 static TRexChar trex_escapechar(TRex *exp)
302     if(*exp->_p == TREX_SYMBOL_ESCAPE_CHAR){
303         exp->_p++;
304         switch(*exp->_p) {
305         case 'v': exp->_p++; return '\v';
306         case 'n': exp->_p++; return '\n';
307         case 't': exp->_p++; return '\t';
308         case 'r': exp->_p++; return '\r';
309         case 'f': exp->_p++; return '\f';
310         default: return (*exp->_p++);
311         }
312     } else if(!scisprint(*exp->_p)) trex_error(exp,_SC("letter expected"));
313     return (*exp->_p++);
316 static int trex_charclass(TRex *exp,int classid)
318     int n = trex_newnode(exp,OP_CCLASS);
319     exp->_nodes[n].left = classid;
320     return n;
323 static int trex_charnode(TRex *exp,TRexBool isclass)
325     TRexChar t;
326     if(*exp->_p == TREX_SYMBOL_ESCAPE_CHAR) {
327         exp->_p++;
328         switch(*exp->_p) {
329             case 'n': exp->_p++; return trex_newnode(exp,'\n');
330             case 't': exp->_p++; return trex_newnode(exp,'\t');
331             case 'r': exp->_p++; return trex_newnode(exp,'\r');
332             case 'f': exp->_p++; return trex_newnode(exp,'\f');
333             case 'v': exp->_p++; return trex_newnode(exp,'\v');
334             case 'a': case 'A': case 'w': case 'W': case 's': case 'S': 
335             case 'd': case 'D': case 'x': case 'X': case 'c': case 'C': 
336             case 'p': case 'P': case 'l': case 'u': 
337                 {
338                 t = *exp->_p; exp->_p++; 
339                 return trex_charclass(exp,t);
340                 }
341             case 'b': 
342             case 'B':
343                 if(!isclass) {
344                     int node = trex_newnode(exp,OP_WB);
345                     exp->_nodes[node].left = *exp->_p;
346                     exp->_p++; 
347                     return node;
348                 } //else default
349             default: 
350                 t = *exp->_p; exp->_p++; 
351                 return trex_newnode(exp,t);
352         }
353     }
354     else if(!scisprint(*exp->_p)) {
355         
356         trex_error(exp,_SC("letter expected"));
357     }
358     t = *exp->_p; exp->_p++; 
359     return trex_newnode(exp,t);
361 static int trex_class(TRex *exp)
363     int ret = -1;
364     int first = -1,chain;
365     if(*exp->_p == TREX_SYMBOL_BEGINNING_OF_STRING){
366         ret = trex_newnode(exp,OP_NCLASS);
367         exp->_p++;
368     }else ret = trex_newnode(exp,OP_CLASS);
369     
370     if(*exp->_p == ']') trex_error(exp,_SC("empty class"));
371     chain = ret;
372     while(*exp->_p != ']' && exp->_p != exp->_eol) {
373         if(*exp->_p == '-' && first != -1){ 
374             int r,t;
375             if(*exp->_p++ == ']') trex_error(exp,_SC("unfinished range"));
376             r = trex_newnode(exp,OP_RANGE);
377             if(first>*exp->_p) trex_error(exp,_SC("invalid range"));
378             if(exp->_nodes[first].type == OP_CCLASS) trex_error(exp,_SC("cannot use character classes in ranges"));
379             exp->_nodes[r].left = exp->_nodes[first].type;
380             t = trex_escapechar(exp);
381             exp->_nodes[r].right = t;
382             exp->_nodes[chain].next = r;
383             chain = r;
384             first = -1;
385         }
386         else{
387             if(first!=-1){
388                 int c = first;
389                 exp->_nodes[chain].next = c;
390                 chain = c;
391                 first = trex_charnode(exp,TRex_True);
392             }
393             else{
394                 first = trex_charnode(exp,TRex_True);
395             }
396         }
397     }
398     if(first!=-1){
399         int c = first;
400         exp->_nodes[chain].next = c;
401         chain = c;
402         first = -1;
403     }
404     /* hack? */
405     exp->_nodes[ret].left = exp->_nodes[ret].next;
406     exp->_nodes[ret].next = -1;
407     return ret;
410 static int trex_parsenumber(TRex *exp)
412     int ret = *exp->_p-'0';
413     int positions = 10;
414     exp->_p++;
415     while(isdigit(*exp->_p)) {
416         ret = ret*10+(*exp->_p++-'0');
417         if(positions==1000000000) trex_error(exp,_SC("overflow in numeric constant"));
418         positions *= 10;
419     };
420     return ret;
423 static int trex_element(TRex *exp)
425     int ret = -1;
426     switch(*exp->_p)
427     {
428     case '(': {
429         int expr,newn;
430         exp->_p++;
433         if(*exp->_p =='?') {
434             exp->_p++;
435             trex_expect(exp,':');
436             expr = trex_newnode(exp,OP_NOCAPEXPR);
437         }
438         else
439             expr = trex_newnode(exp,OP_EXPR);
440         newn = trex_list(exp);
441         exp->_nodes[expr].left = newn;
442         ret = expr;
443         trex_expect(exp,')');
444               }
445               break;
446     case '[':
447         exp->_p++;
448         ret = trex_class(exp);
449         trex_expect(exp,']');
450         break;
451     case TREX_SYMBOL_END_OF_STRING: exp->_p++; ret = trex_newnode(exp,OP_EOL);break;
452     case TREX_SYMBOL_ANY_CHAR: exp->_p++; ret = trex_newnode(exp,OP_DOT);break;
453     default:
454         ret = trex_charnode(exp,TRex_False);
455         break;
456     }
458     {
459         int op;
460         TRexBool isgreedy = TRex_False;
461         unsigned short p0 = 0, p1 = 0;
462         switch(*exp->_p){
463             case TREX_SYMBOL_GREEDY_ZERO_OR_MORE: p0 = 0; p1 = 0xFFFF; exp->_p++; isgreedy = TRex_True; break;
464             case TREX_SYMBOL_GREEDY_ONE_OR_MORE: p0 = 1; p1 = 0xFFFF; exp->_p++; isgreedy = TRex_True; break;
465             case TREX_SYMBOL_GREEDY_ZERO_OR_ONE: p0 = 0; p1 = 1; exp->_p++; isgreedy = TRex_True; break;
466             case '{':
467                 exp->_p++;
468                 if(!isdigit(*exp->_p)) trex_error(exp,_SC("number expected"));
469                 p0 = (unsigned short)trex_parsenumber(exp);
470                 /*******************************/
471                 switch(*exp->_p) {
472             case '}':
473                 p1 = p0; exp->_p++;
474                 break;
475             case ',':
476                 exp->_p++;
477                 p1 = 0xFFFF;
478                 if(isdigit(*exp->_p)){
479                     p1 = (unsigned short)trex_parsenumber(exp);
480                 }
481                 trex_expect(exp,'}');
482                 break;
483             default:
484                 trex_error(exp,_SC(", or } expected"));
485         }
486         /*******************************/
487         isgreedy = TRex_True; 
488         break;
490         }
491         if(isgreedy) {
492             int nnode = trex_newnode(exp,OP_GREEDY);
493             op = OP_GREEDY;
494             exp->_nodes[nnode].left = ret;
495             exp->_nodes[nnode].right = ((p0)<<16)|p1;
496             ret = nnode;
497         }
498     }
499     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')) {
500         int nnode = trex_element(exp);
501         exp->_nodes[ret].next = nnode;
502     }
504     return ret;
507 static int trex_list(TRex *exp)
509     int ret=-1,e;
510     if(*exp->_p == TREX_SYMBOL_BEGINNING_OF_STRING) {
511         exp->_p++;
512         ret = trex_newnode(exp,OP_BOL);
513     }
514     e = trex_element(exp);
515     if(ret != -1) {
516         exp->_nodes[ret].next = e;
517     }
518     else ret = e;
520     if(*exp->_p == TREX_SYMBOL_BRANCH) {
521         int temp,tright;
522         exp->_p++;
523         temp = trex_newnode(exp,OP_OR);
524         exp->_nodes[temp].left = ret;
525         tright = trex_list(exp);
526         exp->_nodes[temp].right = tright;
527         ret = temp;
528     }
529     return ret;
532 static TRexBool trex_matchcclass(int cclass,TRexChar c)
534     switch(cclass) {
535     case 'a': return isalpha(c)?TRex_True:TRex_False;
536     case 'A': return !isalpha(c)?TRex_True:TRex_False;
537     case 'w': return (isalnum(c) || c == '_')?TRex_True:TRex_False;
538     case 'W': return (!isalnum(c) && c != '_')?TRex_True:TRex_False;
539     case 's': return isspace(c)?TRex_True:TRex_False;
540     case 'S': return !isspace(c)?TRex_True:TRex_False;
541     case 'd': return isdigit(c)?TRex_True:TRex_False;
542     case 'D': return !isdigit(c)?TRex_True:TRex_False;
543     case 'x': return isxdigit(c)?TRex_True:TRex_False;
544     case 'X': return !isxdigit(c)?TRex_True:TRex_False;
545     case 'c': return iscntrl(c)?TRex_True:TRex_False;
546     case 'C': return !iscntrl(c)?TRex_True:TRex_False;
547     case 'p': return ispunct(c)?TRex_True:TRex_False;
548     case 'P': return !ispunct(c)?TRex_True:TRex_False;
549     case 'l': return islower(c)?TRex_True:TRex_False;
550     case 'u': return isupper(c)?TRex_True:TRex_False;
551     }
552     return TRex_False; /*cannot happen*/
555 static TRexBool trex_matchclass(TRex* exp,TRexNode *node,TRexChar c)
557     do {
558         switch(node->type) {
559             case OP_RANGE:
560                 if(c >= node->left && c <= node->right) return TRex_True;
561                 break;
562             case OP_CCLASS:
563                 if(trex_matchcclass(node->left,c)) return TRex_True;
564                 break;
565             default:
566                 if(c == node->type)return TRex_True;
567         }
568     } while((node->next != -1) && (node = &exp->_nodes[node->next]));
569     return TRex_False;
572 static const TRexChar *trex_matchnode(TRex* exp,TRexNode *node,const TRexChar *str,TRexNode *next)
574     
575     TRexNodeType type = node->type;
576     switch(type) {
577     case OP_GREEDY: {
578         //TRexNode *greedystop = (node->next != -1) ? &exp->_nodes[node->next] : NULL;
579         TRexNode *greedystop = NULL;
580         int p0 = (node->right >> 16)&0x0000FFFF, p1 = node->right&0x0000FFFF, nmaches = 0;
581         const TRexChar *s=str, *good = str;
583         if(node->next != -1) {
584             greedystop = &exp->_nodes[node->next];
585         }
586         else {
587             greedystop = next;
588         }
590         while((nmaches == 0xFFFF || nmaches < p1)) {
592             const TRexChar *stop;
593             if(!(s = trex_matchnode(exp,&exp->_nodes[node->left],s,greedystop)))
594                 break;
595             nmaches++;
596             good=s;
597             if(greedystop) {
598                 //checks that 0 matches satisfy the expression(if so skips)
599                 //if not would always stop(for instance if is a '?')
600                 if(greedystop->type != OP_GREEDY ||
601                 (greedystop->type == OP_GREEDY && ((greedystop->right >> 16)&0x0000FFFF) != 0))
602                 {
603                     TRexNode *gnext = NULL;
604                     if(greedystop->next != -1) {
605                         gnext = &exp->_nodes[greedystop->next];
606                     }else if(next && next->next != -1){
607                         gnext = &exp->_nodes[next->next];
608                     }
609                     stop = trex_matchnode(exp,greedystop,s,gnext);
610                     if(stop) {
611                         //if satisfied stop it
612                         if(p0 == p1 && p0 == nmaches) break;
613                         else if(nmaches >= p0 && p1 == 0xFFFF) break;
614                         else if(nmaches >= p0 && nmaches <= p1) break;
615                     }
616                 }
617             }
618             
619             if(s >= exp->_eol)
620                 break;
621         }
622         if(p0 == p1 && p0 == nmaches) return good;
623         else if(nmaches >= p0 && p1 == 0xFFFF) return good;
624         else if(nmaches >= p0 && nmaches <= p1) return good;
625         return NULL;
626     }
627     case OP_OR: {
628             const TRexChar *asd = str;
629             TRexNode *temp=&exp->_nodes[node->left];
630             while( (asd = trex_matchnode(exp,temp,asd,NULL)) ) {
631                 if(temp->next != -1)
632                     temp = &exp->_nodes[temp->next];
633                 else
634                     return asd;
635             }
636             asd = str;
637             temp = &exp->_nodes[node->right];
638             while( (asd = trex_matchnode(exp,temp,asd,NULL)) ) {
639                 if(temp->next != -1)
640                     temp = &exp->_nodes[temp->next];
641                 else
642                     return asd;
643             }
644             return NULL;
645             break;
646     }
647     case OP_EXPR:
648     case OP_NOCAPEXPR:{
649             TRexNode *n = &exp->_nodes[node->left];
650             const TRexChar *cur = str;
651             int capture = -1;
652             if(node->type != OP_NOCAPEXPR && node->right == exp->_currsubexp) {
653                 capture = exp->_currsubexp;
654                 exp->_matches[capture].begin = cur;
655                 exp->_currsubexp++;
656             }
657             
658             do {
659                 TRexNode *subnext = NULL;
660                 if(n->next != -1) {
661                     subnext = &exp->_nodes[n->next];
662                 }else {
663                     subnext = next;
664                 }
665                 if(!(cur = trex_matchnode(exp,n,cur,subnext))) {
666                     if(capture != -1){
667                         exp->_matches[capture].begin = 0;
668                         exp->_matches[capture].len = 0;
669                     }
670                     return NULL;
671                 }
672             } while((n->next != -1) && (n = &exp->_nodes[n->next]));
674             if(capture != -1) 
675                 exp->_matches[capture].len = cur - exp->_matches[capture].begin;
676             return cur;
677     }                 
678     case OP_WB:
679         if(str == exp->_bol && !isspace(*str)
680          || (str == exp->_eol && !isspace(*(str-1)))
681          || (!isspace(*str) && isspace(*(str+1)))
682          || (isspace(*str) && !isspace(*(str+1))) ) {
683             return (node->left == 'b')?str:NULL;
684         }
685         return (node->left == 'b')?NULL:str;
686     case OP_BOL:
687         if(str == exp->_bol) return str;
688         return NULL;
689     case OP_EOL:
690         if(str == exp->_eol) return str;
691         return NULL;
692     case OP_DOT:{
693         *str++;
694                 }
695         return str;
696     case OP_NCLASS:
697     case OP_CLASS:
698         if(trex_matchclass(exp,&exp->_nodes[node->left],*str)?(type == OP_CLASS?TRex_True:TRex_False):(type == OP_NCLASS?TRex_True:TRex_False)) {
699             *str++;
700             return str;
701         }
702         return NULL;
703     case OP_CCLASS:
704         if(trex_matchcclass(node->left,*str)) {
705             *str++;
706             return str;
707         }
708         return NULL;
709     default: /* char */
710         if(*str != node->type) return NULL;
711         *str++;
712         return str;
713     }
714     return NULL;
717 /* public api */
718 TRex *trex_compile(const TRexChar *pattern,const TRexChar **error)
720     TRex *exp = (TRex *)malloc(sizeof(TRex));
721     exp->_eol = exp->_bol = NULL;
722     exp->_p = pattern;
723     exp->_nallocated = (int)scstrlen(pattern) * sizeof(TRexChar);
724     exp->_nodes = (TRexNode *)malloc(exp->_nallocated * sizeof(TRexNode));
725     exp->_nsize = 0;
726     exp->_matches = 0;
727     exp->_nsubexpr = 0;
728     exp->_first = trex_newnode(exp,OP_EXPR);
729     exp->_error = error;
730     exp->_jmpbuf = malloc(sizeof(jmp_buf));
731     if(setjmp(*((jmp_buf*)exp->_jmpbuf)) == 0) {
732         int res = trex_list(exp);
733         exp->_nodes[exp->_first].left = res;
734         if(*exp->_p!='\0')
735             trex_error(exp,_SC("unexpected character"));
736 #ifdef _DEBUG
737         {
738             int nsize,i;
739             TRexNode *t;
740             nsize = exp->_nsize;
741             t = &exp->_nodes[0];
742             scprintf(_SC("\n"));
743             for(i = 0;i < nsize; i++) {
744                 if(exp->_nodes[i].type>MAX_CHAR)
745                     scprintf(_SC("[%02d] %10s "),i,g_nnames[exp->_nodes[i].type-MAX_CHAR]);
746                 else
747                     scprintf(_SC("[%02d] %10c "),i,exp->_nodes[i].type);
748                 scprintf(_SC("left %02d right %02d next %02d\n"),exp->_nodes[i].left,exp->_nodes[i].right,exp->_nodes[i].next);
749             }
750             scprintf(_SC("\n"));
751         }
752 #endif
753         exp->_matches = (TRexMatch *) malloc(exp->_nsubexpr * sizeof(TRexMatch));
754         memset(exp->_matches,0,exp->_nsubexpr * sizeof(TRexMatch));
755     }
756     else{
757         trex_free(exp);
758         return NULL;
759     }
760     return exp;
763 void trex_free(TRex *exp)
765     if(exp)    {
766         if(exp->_nodes) free(exp->_nodes);
767         if(exp->_jmpbuf) free(exp->_jmpbuf);
768         if(exp->_matches) free(exp->_matches);
769         free(exp);
770     }
773 TRexBool trex_match(TRex* exp,const TRexChar* text)
775     const TRexChar* res = NULL;
776     exp->_bol = text;
777     exp->_eol = text + scstrlen(text);
778     exp->_currsubexp = 0;
779     res = trex_matchnode(exp,exp->_nodes,text,NULL);
780     if(res == NULL || res != exp->_eol)
781         return TRex_False;
782     return TRex_True;
785 TRexBool trex_searchrange(TRex* exp,const TRexChar* text_begin,const TRexChar* text_end,const TRexChar** out_begin, const TRexChar** out_end)
787     const TRexChar *cur = NULL;
788     int node = exp->_first;
789     if(text_begin >= text_end) return TRex_False;
790     exp->_bol = text_begin;
791     exp->_eol = text_end;
792     do {
793         cur = text_begin;
794         while(node != -1) {
795             exp->_currsubexp = 0;
796             cur = trex_matchnode(exp,&exp->_nodes[node],cur,NULL);
797             if(!cur)
798                 break;
799             node = exp->_nodes[node].next;
800         }
801         *text_begin++;
802     } while(cur == NULL && text_begin != text_end);
804     if(cur == NULL)
805         return TRex_False;
807     --text_begin;
809     if(out_begin) *out_begin = text_begin;
810     if(out_end) *out_end = cur;
811     return TRex_True;
814 TRexBool trex_search(TRex* exp,const TRexChar* text, const TRexChar** out_begin, const TRexChar** out_end)
816     return trex_searchrange(exp,text,text + scstrlen(text),out_begin,out_end);
819 int trex_getsubexpcount(TRex* exp)
821     return exp->_nsubexpr;
824 TRexBool trex_getsubexp(TRex* exp, int n, TRexMatch *subexp)
826     if( n<0 || n >= exp->_nsubexpr) return TRex_False;
827     *subexp = exp->_matches[n];
828     return TRex_True;
832 //########################################################################
833 //########################################################################
834 //##  E N D    R E G E X P
835 //########################################################################
836 //########################################################################
842 //########################################################################
843 //########################################################################
844 //##  X M L
845 //########################################################################
846 //########################################################################
848 // Note:  This mini-dom library comes from Pedro, another little project
849 // of mine.
851 typedef std::string String;
852 typedef unsigned int XMLCh;
855 class Namespace
857 public:
858     Namespace()
859         {}
861     Namespace(const String &prefixArg, const String &namespaceURIArg)
862         {
863         prefix       = prefixArg;
864         namespaceURI = namespaceURIArg;
865         }
867     Namespace(const Namespace &other)
868         {
869         assign(other);
870         }
872     Namespace &operator=(const Namespace &other)
873         {
874         assign(other);
875         return *this;
876         }
878     virtual ~Namespace()
879         {}
881     virtual String getPrefix()
882         { return prefix; }
884     virtual String getNamespaceURI()
885         { return namespaceURI; }
887 protected:
889     void assign(const Namespace &other)
890         {
891         prefix       = other.prefix;
892         namespaceURI = other.namespaceURI;
893         }
895     String prefix;
896     String namespaceURI;
898 };
900 class Attribute
902 public:
903     Attribute()
904         {}
906     Attribute(const String &nameArg, const String &valueArg)
907         {
908         name  = nameArg;
909         value = valueArg;
910         }
912     Attribute(const Attribute &other)
913         {
914         assign(other);
915         }
917     Attribute &operator=(const Attribute &other)
918         {
919         assign(other);
920         return *this;
921         }
923     virtual ~Attribute()
924         {}
926     virtual String getName()
927         { return name; }
929     virtual String getValue()
930         { return value; }
932 protected:
934     void assign(const Attribute &other)
935         {
936         name  = other.name;
937         value = other.value;
938         }
940     String name;
941     String value;
943 };
946 class Element
948 friend class Parser;
950 public:
951     Element()
952         {
953         parent = NULL;
954         }
956     Element(const String &nameArg)
957         {
958         parent = NULL;
959         name   = nameArg;
960         }
962     Element(const String &nameArg, const String &valueArg)
963         {
964         parent = NULL;
965         name   = nameArg;
966         value  = valueArg;
967         }
969     Element(const Element &other)
970         {
971         assign(other);
972         }
974     Element &operator=(const Element &other)
975         {
976         assign(other);
977         return *this;
978         }
980     virtual Element *clone();
982     virtual ~Element()
983         {
984         for (unsigned int i=0 ; i<children.size() ; i++)
985             delete children[i];
986         }
988     virtual String getName()
989         { return name; }
991     virtual String getValue()
992         { return value; }
994     Element *getParent()
995         { return parent; }
997     std::vector<Element *> getChildren()
998         { return children; }
1000     std::vector<Element *> findElements(const String &name);
1002     String getAttribute(const String &name);
1004     std::vector<Attribute> &getAttributes()
1005         { return attributes; } 
1007     String getTagAttribute(const String &tagName, const String &attrName);
1009     String getTagValue(const String &tagName);
1011     void addChild(Element *child);
1013     void addAttribute(const String &name, const String &value);
1015     void addNamespace(const String &prefix, const String &namespaceURI);
1018     /**
1019      * Prettyprint an XML tree to an output stream.  Elements are indented
1020      * according to element hierarchy.
1021      * @param f a stream to receive the output
1022      * @param elem the element to output
1023      */
1024     void writeIndented(FILE *f);
1026     /**
1027      * Prettyprint an XML tree to standard output.  This is the equivalent of
1028      * writeIndented(stdout).
1029      * @param elem the element to output
1030      */
1031     void print();
1033 protected:
1035     void assign(const Element &other)
1036         {
1037         parent     = other.parent;
1038         children   = other.children;
1039         attributes = other.attributes;
1040         namespaces = other.namespaces;
1041         name       = other.name;
1042         value      = other.value;
1043         }
1045     void findElementsRecursive(std::vector<Element *>&res, const String &name);
1047     void writeIndentedRecursive(FILE *f, int indent);
1049     Element *parent;
1051     std::vector<Element *>children;
1053     std::vector<Attribute> attributes;
1054     std::vector<Namespace> namespaces;
1056     String name;
1057     String value;
1059 };
1065 class Parser
1067 public:
1068     /**
1069      * Constructor
1070      */
1071     Parser()
1072         { init(); }
1074     virtual ~Parser()
1075         {}
1077     /**
1078      * Parse XML in a char buffer.
1079      * @param buf a character buffer to parse
1080      * @param pos position to start parsing
1081      * @param len number of chars, from pos, to parse.
1082      * @return a pointer to the root of the XML document;
1083      */
1084     Element *parse(const char *buf,int pos,int len);
1086     /**
1087      * Parse XML in a char buffer.
1088      * @param buf a character buffer to parse
1089      * @param pos position to start parsing
1090      * @param len number of chars, from pos, to parse.
1091      * @return a pointer to the root of the XML document;
1092      */
1093     Element *parse(const String &buf);
1095     /**
1096      * Parse a named XML file.  The file is loaded like a data file;
1097      * the original format is not preserved.
1098      * @param fileName the name of the file to read
1099      * @return a pointer to the root of the XML document;
1100      */
1101     Element *parseFile(const String &fileName);
1103     /**
1104      * Utility method to preprocess a string for XML
1105      * output, escaping its entities.
1106      * @param str the string to encode
1107      */
1108     static String encode(const String &str);
1110     /**
1111      *  Removes whitespace from beginning and end of a string
1112      */
1113     String trim(const String &s);
1115 private:
1117     void init()
1118         {
1119         keepGoing       = true;
1120         currentNode     = NULL;
1121         parselen        = 0;
1122         parsebuf        = NULL;
1123         currentPosition = 0;
1124         }
1126     void getLineAndColumn(long pos, long *lineNr, long *colNr);
1128     void error(char *fmt, ...);
1130     int peek(long pos);
1132     int match(long pos, const char *text);
1134     int skipwhite(long p);
1136     int getWord(int p0, String &buf);
1138     int getQuoted(int p0, String &buf, int do_i_parse);
1140     int parseVersion(int p0);
1142     int parseDoctype(int p0);
1144     int parseElement(int p0, Element *par,int depth);
1146     Element *parse(XMLCh *buf,int pos,int len);
1148     bool       keepGoing;
1149     Element    *currentNode;
1150     long       parselen;
1151     XMLCh      *parsebuf;
1152     String  cdatabuf;
1153     long       currentPosition;
1154     int        colNr;
1156 };
1161 //########################################################################
1162 //# E L E M E N T
1163 //########################################################################
1165 Element *Element::clone()
1167     Element *elem = new Element(name, value);
1168     elem->parent     = parent;
1169     elem->attributes = attributes;
1170     elem->namespaces = namespaces;
1172     std::vector<Element *>::iterator iter;
1173     for (iter = children.begin(); iter != children.end() ; iter++)
1174         {
1175         elem->addChild((*iter)->clone());
1176         }
1177     return elem;
1181 void Element::findElementsRecursive(std::vector<Element *>&res, const String &name)
1183     if (getName() == name)
1184         {
1185         res.push_back(this);
1186         }
1187     for (unsigned int i=0; i<children.size() ; i++)
1188         children[i]->findElementsRecursive(res, name);
1191 std::vector<Element *> Element::findElements(const String &name)
1193     std::vector<Element *> res;
1194     findElementsRecursive(res, name);
1195     return res;
1198 String Element::getAttribute(const String &name)
1200     for (unsigned int i=0 ; i<attributes.size() ; i++)
1201         if (attributes[i].getName() ==name)
1202             return attributes[i].getValue();
1203     return "";
1206 String Element::getTagAttribute(const String &tagName, const String &attrName)
1208     std::vector<Element *>elems = findElements(tagName);
1209     if (elems.size() <1)
1210         return "";
1211     String res = elems[0]->getAttribute(attrName);
1212     return res;
1215 String Element::getTagValue(const String &tagName)
1217     std::vector<Element *>elems = findElements(tagName);
1218     if (elems.size() <1)
1219         return "";
1220     String res = elems[0]->getValue();
1221     return res;
1224 void Element::addChild(Element *child)
1226     if (!child)
1227         return;
1228     child->parent = this;
1229     children.push_back(child);
1233 void Element::addAttribute(const String &name, const String &value)
1235     Attribute attr(name, value);
1236     attributes.push_back(attr);
1239 void Element::addNamespace(const String &prefix, const String &namespaceURI)
1241     Namespace ns(prefix, namespaceURI);
1242     namespaces.push_back(ns);
1245 void Element::writeIndentedRecursive(FILE *f, int indent)
1247     int i;
1248     if (!f)
1249         return;
1250     //Opening tag, and attributes
1251     for (i=0;i<indent;i++)
1252         fputc(' ',f);
1253     fprintf(f,"<%s",name.c_str());
1254     for (unsigned int i=0 ; i<attributes.size() ; i++)
1255         {
1256         fprintf(f," %s=\"%s\"",
1257               attributes[i].getName().c_str(),
1258               attributes[i].getValue().c_str());
1259         }
1260     for (unsigned int i=0 ; i<namespaces.size() ; i++)
1261         {
1262         fprintf(f," xmlns:%s=\"%s\"",
1263               namespaces[i].getPrefix().c_str(),
1264               namespaces[i].getNamespaceURI().c_str());
1265         }
1266     fprintf(f,">\n");
1268     //Between the tags
1269     if (value.size() > 0)
1270         {
1271         for (int i=0;i<indent;i++)
1272             fputc(' ', f);
1273         fprintf(f," %s\n", value.c_str());
1274         }
1276     for (unsigned int i=0 ; i<children.size() ; i++)
1277         children[i]->writeIndentedRecursive(f, indent+2);
1279     //Closing tag
1280     for (int i=0; i<indent; i++)
1281         fputc(' ',f);
1282     fprintf(f,"</%s>\n", name.c_str());
1285 void Element::writeIndented(FILE *f)
1287     writeIndentedRecursive(f, 0);
1290 void Element::print()
1292     writeIndented(stdout);
1296 //########################################################################
1297 //# P A R S E R
1298 //########################################################################
1302 typedef struct
1303     {
1304     char *escaped;
1305     char value;
1306     } EntityEntry;
1308 static EntityEntry entities[] =
1310     { "&amp;" , '&'  },
1311     { "&lt;"  , '<'  },
1312     { "&gt;"  , '>'  },
1313     { "&apos;", '\'' },
1314     { "&quot;", '"'  },
1315     { NULL    , '\0' }
1316 };
1320 /**
1321  *  Removes whitespace from beginning and end of a string
1322  */
1323 String Parser::trim(const String &s)
1325     if (s.size() < 1)
1326         return s;
1327     
1328     //Find first non-ws char
1329     unsigned int begin = 0;
1330     for ( ; begin < s.size() ; begin++)
1331         {
1332         if (!isspace(s[begin]))
1333             break;
1334         }
1336     //Find first non-ws char, going in reverse
1337     unsigned int end = s.size() - 1;
1338     for ( ; end > begin ; end--)
1339         {
1340         if (!isspace(s[end]))
1341             break;
1342         }
1343     //trace("begin:%d  end:%d", begin, end);
1345     String res = s.substr(begin, end-begin+1);
1346     return res;
1349 void Parser::getLineAndColumn(long pos, long *lineNr, long *colNr)
1351     long line = 1;
1352     long col  = 1;
1353     for (long i=0 ; i<pos ; i++)
1354         {
1355         XMLCh ch = parsebuf[i];
1356         if (ch == '\n' || ch == '\r')
1357             {
1358             col = 0;
1359             line ++;
1360             }
1361         else
1362             col++;
1363         }
1364     *lineNr = line;
1365     *colNr  = col;
1370 void Parser::error(char *fmt, ...)
1372     long lineNr;
1373     long colNr;
1374     getLineAndColumn(currentPosition, &lineNr, &colNr);
1375     va_list args;
1376     fprintf(stderr, "xml error at line %ld, column %ld:", lineNr, colNr);
1377     va_start(args,fmt);
1378     vfprintf(stderr,fmt,args);
1379     va_end(args) ;
1380     fprintf(stderr, "\n");
1385 int Parser::peek(long pos)
1387     if (pos >= parselen)
1388         return -1;
1389     currentPosition = pos;
1390     int ch = parsebuf[pos];
1391     //printf("ch:%c\n", ch);
1392     return ch;
1397 String Parser::encode(const String &str)
1399     String ret;
1400     for (unsigned int i=0 ; i<str.size() ; i++)
1401         {
1402         XMLCh ch = (XMLCh)str[i];
1403         if (ch == '&')
1404             ret.append("&amp;");
1405         else if (ch == '<')
1406             ret.append("&lt;");
1407         else if (ch == '>')
1408             ret.append("&gt;");
1409         else if (ch == '\'')
1410             ret.append("&apos;");
1411         else if (ch == '"')
1412             ret.append("&quot;");
1413         else
1414             ret.push_back(ch);
1416         }
1417     return ret;
1421 int Parser::match(long p0, const char *text)
1423     int p = p0;
1424     while (*text)
1425         {
1426         if (peek(p) != *text)
1427             return p0;
1428         p++; text++;
1429         }
1430     return p;
1435 int Parser::skipwhite(long p)
1438     while (p<parselen)
1439         {
1440         int p2 = match(p, "<!--");
1441         if (p2 > p)
1442             {
1443             p = p2;
1444             while (p<parselen)
1445               {
1446               p2 = match(p, "-->");
1447               if (p2 > p)
1448                   {
1449                   p = p2;
1450                   break;
1451                   }
1452               p++;
1453               }
1454           }
1455       XMLCh b = peek(p);
1456       if (!isspace(b))
1457           break;
1458       p++;
1459       }
1460   return p;
1463 /* modify this to allow all chars for an element or attribute name*/
1464 int Parser::getWord(int p0, String &buf)
1466     int p = p0;
1467     while (p<parselen)
1468         {
1469         XMLCh b = peek(p);
1470         if (b<=' ' || b=='/' || b=='>' || b=='=')
1471             break;
1472         buf.push_back(b);
1473         p++;
1474         }
1475     return p;
1478 int Parser::getQuoted(int p0, String &buf, int do_i_parse)
1481     int p = p0;
1482     if (peek(p) != '"' && peek(p) != '\'')
1483         return p0;
1484     p++;
1486     while ( p<parselen )
1487         {
1488         XMLCh b = peek(p);
1489         if (b=='"' || b=='\'')
1490             break;
1491         if (b=='&' && do_i_parse)
1492             {
1493             bool found = false;
1494             for (EntityEntry *ee = entities ; ee->value ; ee++)
1495                 {
1496                 int p2 = match(p, ee->escaped);
1497                 if (p2>p)
1498                     {
1499                     buf.push_back(ee->value);
1500                     p = p2;
1501                     found = true;
1502                     break;
1503                     }
1504                 }
1505             if (!found)
1506                 {
1507                 error("unterminated entity");
1508                 return false;
1509                 }
1510             }
1511         else
1512             {
1513             buf.push_back(b);
1514             p++;
1515             }
1516         }
1517     return p;
1520 int Parser::parseVersion(int p0)
1522     //printf("### parseVersion: %d\n", p0);
1524     int p = p0;
1526     p = skipwhite(p0);
1528     if (peek(p) != '<')
1529         return p0;
1531     p++;
1532     if (p>=parselen || peek(p)!='?')
1533         return p0;
1535     p++;
1537     String buf;
1539     while (p<parselen)
1540         {
1541         XMLCh ch = peek(p);
1542         if (ch=='?')
1543             {
1544             p++;
1545             break;
1546             }
1547         buf.push_back(ch);
1548         p++;
1549         }
1551     if (peek(p) != '>')
1552         return p0;
1553     p++;
1555     //printf("Got version:%s\n",buf.c_str());
1556     return p;
1559 int Parser::parseDoctype(int p0)
1561     //printf("### parseDoctype: %d\n", p0);
1563     int p = p0;
1564     p = skipwhite(p);
1566     if (p>=parselen || peek(p)!='<')
1567         return p0;
1569     p++;
1571     if (peek(p)!='!' || peek(p+1)=='-')
1572         return p0;
1573     p++;
1575     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     //printf("Got doctype:%s\n",buf.c_str());
1589     return p;
1592 int Parser::parseElement(int p0, Element *par,int depth)
1595     int p = p0;
1597     int p2 = p;
1599     p = skipwhite(p);
1601     //## Get open tag
1602     XMLCh ch = peek(p);
1603     if (ch!='<')
1604         return p0;
1606     p++;
1608     String openTagName;
1609     p = skipwhite(p);
1610     p = getWord(p, openTagName);
1611     //printf("####tag :%s\n", openTagName.c_str());
1612     p = skipwhite(p);
1614     //Add element to tree
1615     Element *n = new Element(openTagName);
1616     n->parent = par;
1617     par->addChild(n);
1619     // Get attributes
1620     if (peek(p) != '>')
1621         {
1622         while (p<parselen)
1623             {
1624             p = skipwhite(p);
1625             ch = peek(p);
1626             //printf("ch:%c\n",ch);
1627             if (ch=='>')
1628                 break;
1629             else if (ch=='/' && p<parselen+1)
1630                 {
1631                 p++;
1632                 p = skipwhite(p);
1633                 ch = peek(p);
1634                 if (ch=='>')
1635                     {
1636                     p++;
1637                     //printf("quick close\n");
1638                     return p;
1639                     }
1640                 }
1641             String attrName;
1642             p2 = getWord(p, attrName);
1643             if (p2==p)
1644                 break;
1645             //printf("name:%s",buf);
1646             p=p2;
1647             p = skipwhite(p);
1648             ch = peek(p);
1649             //printf("ch:%c\n",ch);
1650             if (ch!='=')
1651                 break;
1652             p++;
1653             p = skipwhite(p);
1654             // ch = parsebuf[p];
1655             // printf("ch:%c\n",ch);
1656             String attrVal;
1657             p2 = getQuoted(p, attrVal, true);
1658             p=p2+1;
1659             //printf("name:'%s'   value:'%s'\n",attrName.c_str(),attrVal.c_str());
1660             char *namestr = (char *)attrName.c_str();
1661             if (strncmp(namestr, "xmlns:", 6)==0)
1662                 n->addNamespace(attrName, attrVal);
1663             else
1664                 n->addAttribute(attrName, attrVal);
1665             }
1666         }
1668     bool cdata = false;
1670     p++;
1671     // ### Get intervening data ### */
1672     String data;
1673     while (p<parselen)
1674         {
1675         //# COMMENT
1676         p2 = match(p, "<!--");
1677         if (!cdata && p2>p)
1678             {
1679             p = p2;
1680             while (p<parselen)
1681                 {
1682                 p2 = match(p, "-->");
1683                 if (p2 > p)
1684                     {
1685                     p = p2;
1686                     break;
1687                     }
1688                 p++;
1689                 }
1690             }
1692         ch = peek(p);
1693         //# END TAG
1694         if (ch=='<' && !cdata && peek(p+1)=='/')
1695             {
1696             break;
1697             }
1698         //# CDATA
1699         p2 = match(p, "<![CDATA[");
1700         if (p2 > p)
1701             {
1702             cdata = true;
1703             p = p2;
1704             continue;
1705             }
1707         //# CHILD ELEMENT
1708         if (ch == '<')
1709             {
1710             p2 = parseElement(p, n, depth+1);
1711             if (p2 == p)
1712                 {
1713                 /*
1714                 printf("problem on element:%s.  p2:%d p:%d\n",
1715                       openTagName.c_str(), p2, p);
1716                 */
1717                 return p0;
1718                 }
1719             p = p2;
1720             continue;
1721             }
1722         //# ENTITY
1723         if (ch=='&' && !cdata)
1724             {
1725             bool found = false;
1726             for (EntityEntry *ee = entities ; ee->value ; ee++)
1727                 {
1728                 int p2 = match(p, ee->escaped);
1729                 if (p2>p)
1730                     {
1731                     data.push_back(ee->value);
1732                     p = p2;
1733                     found = true;
1734                     break;
1735                     }
1736                 }
1737             if (!found)
1738                 {
1739                 error("unterminated entity");
1740                 return -1;
1741                 }
1742             continue;
1743             }
1745         //# NONE OF THE ABOVE
1746         data.push_back(ch);
1747         p++;
1748         }/*while*/
1751     n->value = data;
1752     //printf("%d : data:%s\n",p,data.c_str());
1754     //## Get close tag
1755     p = skipwhite(p);
1756     ch = peek(p);
1757     if (ch != '<')
1758         {
1759         error("no < for end tag\n");
1760         return p0;
1761         }
1762     p++;
1763     ch = peek(p);
1764     if (ch != '/')
1765         {
1766         error("no / on end tag");
1767         return p0;
1768         }
1769     p++;
1770     ch = peek(p);
1771     p = skipwhite(p);
1772     String closeTagName;
1773     p = getWord(p, closeTagName);
1774     if (openTagName != closeTagName)
1775         {
1776         error("Mismatched closing tag.  Expected </%S>. Got '%S'.",
1777                 openTagName.c_str(), closeTagName.c_str());
1778         return p0;
1779         }
1780     p = skipwhite(p);
1781     if (peek(p) != '>')
1782         {
1783         error("no > on end tag for '%s'", closeTagName.c_str());
1784         return p0;
1785         }
1786     p++;
1787     // printf("close element:%s\n",closeTagName.c_str());
1788     p = skipwhite(p);
1789     return p;
1795 Element *Parser::parse(XMLCh *buf,int pos,int len)
1797     parselen = len;
1798     parsebuf = buf;
1799     Element *rootNode = new Element("root");
1800     pos = parseVersion(pos);
1801     pos = parseDoctype(pos);
1802     pos = parseElement(pos, rootNode, 0);
1803     return rootNode;
1807 Element *Parser::parse(const char *buf, int pos, int len)
1809     XMLCh *charbuf = new XMLCh[len + 1];
1810     long i = 0;
1811     for ( ; i < len ; i++)
1812         charbuf[i] = (XMLCh)buf[i];
1813     charbuf[i] = '\0';
1815     Element *n = parse(charbuf, pos, len);
1816     delete[] charbuf;
1817     return n;
1820 Element *Parser::parse(const String &buf)
1822     long len = (long)buf.size();
1823     XMLCh *charbuf = new XMLCh[len + 1];
1824     long i = 0;
1825     for ( ; i < len ; i++)
1826         charbuf[i] = (XMLCh)buf[i];
1827     charbuf[i] = '\0';
1829     Element *n = parse(charbuf, 0, len);
1830     delete[] charbuf;
1831     return n;
1834 Element *Parser::parseFile(const String &fileName)
1837     //##### LOAD INTO A CHAR BUF, THEN CONVERT TO XMLCh
1838     FILE *f = fopen(fileName.c_str(), "rb");
1839     if (!f)
1840         return NULL;
1842     struct stat  statBuf;
1843     if (fstat(fileno(f),&statBuf)<0)
1844         {
1845         fclose(f);
1846         return NULL;
1847         }
1848     long filelen = statBuf.st_size;
1850     //printf("length:%d\n",filelen);
1851     XMLCh *charbuf = new XMLCh[filelen + 1];
1852     for (XMLCh *p=charbuf ; !feof(f) ; p++)
1853         {
1854         *p = (XMLCh)fgetc(f);
1855         }
1856     fclose(f);
1857     charbuf[filelen] = '\0';
1860     /*
1861     printf("nrbytes:%d\n",wc_count);
1862     printf("buf:%ls\n======\n",charbuf);
1863     */
1864     Element *n = parse(charbuf, 0, filelen);
1865     delete[] charbuf;
1866     return n;
1871 //########################################################################
1872 //########################################################################
1873 //##  E N D    X M L
1874 //########################################################################
1875 //########################################################################
1878 //########################################################################
1879 //########################################################################
1880 //##  U R I
1881 //########################################################################
1882 //########################################################################
1884 //This would normally be a call to a UNICODE function
1885 #define isLetter(x) isalpha(x)
1887 /**
1888  *  A class that implements the W3C URI resource reference.
1889  */
1890 class URI
1892 public:
1894     typedef enum
1895         {
1896         SCHEME_NONE =0,
1897         SCHEME_DATA,
1898         SCHEME_HTTP,
1899         SCHEME_HTTPS,
1900         SCHEME_FTP,
1901         SCHEME_FILE,
1902         SCHEME_LDAP,
1903         SCHEME_MAILTO,
1904         SCHEME_NEWS,
1905         SCHEME_TELNET
1906         } SchemeTypes;
1908     /**
1909      *
1910      */
1911     URI()
1912         {
1913         init();
1914         }
1916     /**
1917      *
1918      */
1919     URI(const String &str)
1920         {
1921         init();
1922         parse(str);
1923         }
1926     /**
1927      *
1928      */
1929     URI(const char *str)
1930         {
1931         init();
1932         String domStr = str;
1933         parse(domStr);
1934         }
1937     /**
1938      *
1939      */
1940     URI(const URI &other)
1941         {
1942         init();
1943         assign(other);
1944         }
1947     /**
1948      *
1949      */
1950     URI &operator=(const URI &other)
1951         {
1952         init();
1953         assign(other);
1954         return *this;
1955         }
1958     /**
1959      *
1960      */
1961     virtual ~URI()
1962         {}
1966     /**
1967      *
1968      */
1969     virtual bool parse(const String &str);
1971     /**
1972      *
1973      */
1974     virtual String toString() const;
1976     /**
1977      *
1978      */
1979     virtual int getScheme() const;
1981     /**
1982      *
1983      */
1984     virtual String getSchemeStr() const;
1986     /**
1987      *
1988      */
1989     virtual String getAuthority() const;
1991     /**
1992      *  Same as getAuthority, but if the port has been specified
1993      *  as host:port , the port will not be included
1994      */
1995     virtual String getHost() const;
1997     /**
1998      *
1999      */
2000     virtual int getPort() const;
2002     /**
2003      *
2004      */
2005     virtual String getPath() const;
2007     /**
2008      *
2009      */
2010     virtual String getNativePath() const;
2012     /**
2013      *
2014      */
2015     virtual bool isAbsolute() const;
2017     /**
2018      *
2019      */
2020     virtual bool isOpaque() const;
2022     /**
2023      *
2024      */
2025     virtual String getQuery() const;
2027     /**
2028      *
2029      */
2030     virtual String getFragment() const;
2032     /**
2033      *
2034      */
2035     virtual URI resolve(const URI &other) const;
2037     /**
2038      *
2039      */
2040     virtual void normalize();
2042 private:
2044     /**
2045      *
2046      */
2047     void init()
2048         {
2049         parsebuf  = NULL;
2050         parselen  = 0;
2051         scheme    = SCHEME_NONE;
2052         schemeStr = "";
2053         port      = 0;
2054         authority = "";
2055         path      = "";
2056         absolute  = false;
2057         opaque    = false;
2058         query     = "";
2059         fragment  = "";
2060         }
2063     /**
2064      *
2065      */
2066     void assign(const URI &other)
2067         {
2068         scheme    = other.scheme;
2069         schemeStr = other.schemeStr;
2070         authority = other.authority;
2071         port      = other.port;
2072         path      = other.path;
2073         absolute  = other.absolute;
2074         opaque    = other.opaque;
2075         query     = other.query;
2076         fragment  = other.fragment;
2077         }
2079     int scheme;
2081     String schemeStr;
2083     String authority;
2085     bool portSpecified;
2087     int port;
2089     String path;
2091     bool absolute;
2093     bool opaque;
2095     String query;
2097     String fragment;
2099     void error(const char *fmt, ...);
2101     void trace(const char *fmt, ...);
2104     int peek(int p);
2106     int match(int p, char *key);
2108     int parseScheme(int p);
2110     int parseHierarchicalPart(int p0);
2112     int parseQuery(int p0);
2114     int parseFragment(int p0);
2116     int parse(int p);
2118     char *parsebuf;
2120     int parselen;
2122 };
2126 typedef struct
2128     int  ival;
2129     char *sval;
2130     int  port;
2131 } LookupEntry;
2133 LookupEntry schemes[] =
2135     { URI::SCHEME_DATA,   "data:",    0 },
2136     { URI::SCHEME_HTTP,   "http:",   80 },
2137     { URI::SCHEME_HTTPS,  "https:", 443 },
2138     { URI::SCHEME_FTP,    "ftp",     12 },
2139     { URI::SCHEME_FILE,   "file:",    0 },
2140     { URI::SCHEME_LDAP,   "ldap:",  123 },
2141     { URI::SCHEME_MAILTO, "mailto:", 25 },
2142     { URI::SCHEME_NEWS,   "news:",  117 },
2143     { URI::SCHEME_TELNET, "telnet:", 23 },
2144     { 0,                  NULL,       0 }
2145 };
2148 String URI::toString() const
2150     String str = schemeStr;
2151     if (authority.size() > 0)
2152         {
2153         str.append("//");
2154         str.append(authority);
2155         }
2156     str.append(path);
2157     if (query.size() > 0)
2158         {
2159         str.append("?");
2160         str.append(query);
2161         }
2162     if (fragment.size() > 0)
2163         {
2164         str.append("#");
2165         str.append(fragment);
2166         }
2167     return str;
2171 int URI::getScheme() const
2173     return scheme;
2176 String URI::getSchemeStr() const
2178     return schemeStr;
2182 String URI::getAuthority() const
2184     String ret = authority;
2185     if (portSpecified && port>=0)
2186         {
2187         char buf[7];
2188         snprintf(buf, 6, ":%6d", port);
2189         ret.append(buf);
2190         }
2191     return ret;
2194 String URI::getHost() const
2196     return authority;
2199 int URI::getPort() const
2201     return port;
2205 String URI::getPath() const
2207     return path;
2210 String URI::getNativePath() const
2212     String npath;
2213 #ifdef __WIN32__
2214     unsigned int firstChar = 0;
2215     if (path.size() >= 3)
2216         {
2217         if (path[0] == '/' &&
2218             isLetter(path[1]) &&
2219             path[2] == ':')
2220             firstChar++;
2221          }
2222     for (unsigned int i=firstChar ; i<path.size() ; i++)
2223         {
2224         XMLCh ch = (XMLCh) path[i];
2225         if (ch == '/')
2226             npath.push_back((XMLCh)'\\');
2227         else
2228             npath.push_back(ch);
2229         }
2230 #else
2231     npath = path;
2232 #endif
2233     return npath;
2237 bool URI::isAbsolute() const
2239     return absolute;
2242 bool URI::isOpaque() const
2244     return opaque;
2248 String URI::getQuery() const
2250     return query;
2254 String URI::getFragment() const
2256     return fragment;
2260 URI URI::resolve(const URI &other) const
2262     //### According to w3c, this is handled in 3 cases
2264     //## 1
2265     if (opaque || other.isAbsolute())
2266         return other;
2268     //## 2
2269     if (other.fragment.size()  >  0 &&
2270         other.path.size()      == 0 &&
2271         other.scheme           == SCHEME_NONE &&
2272         other.authority.size() == 0 &&
2273         other.query.size()     == 0 )
2274         {
2275         URI fragUri = *this;
2276         fragUri.fragment = other.fragment;
2277         return fragUri;
2278         }
2280     //## 3 http://www.ietf.org/rfc/rfc2396.txt, section 5.2
2281     URI newUri;
2282     //# 3.1
2283     newUri.scheme    = scheme;
2284     newUri.schemeStr = schemeStr;
2285     newUri.query     = other.query;
2286     newUri.fragment  = other.fragment;
2287     if (other.authority.size() > 0)
2288         {
2289         //# 3.2
2290         if (absolute || other.absolute)
2291             newUri.absolute = true;
2292         newUri.authority = other.authority;
2293         newUri.port      = other.port;//part of authority
2294         newUri.path      = other.path;
2295         }
2296     else
2297         {
2298         //# 3.3
2299         if (other.absolute)
2300             {
2301             newUri.absolute = true;
2302             newUri.path     = other.path;
2303             }
2304         else
2305             {
2306             unsigned int pos = path.find_last_of('/');
2307             if (pos != path.npos)
2308                 {
2309                 String tpath = path.substr(0, pos+1);
2310                 tpath.append(other.path);
2311                 newUri.path = tpath;
2312                 }
2313             else
2314                 newUri.path = other.path;
2315             }
2316         }
2318     newUri.normalize();
2319     return newUri;
2323 /**
2324  *  This follows the Java URI algorithm:
2325  *   1. All "." segments are removed.
2326  *   2. If a ".." segment is preceded by a non-".." segment
2327  *          then both of these segments are removed. This step
2328  *          is repeated until it is no longer applicable.
2329  *   3. If the path is relative, and if its first segment
2330  *          contains a colon character (':'), then a "." segment
2331  *          is prepended. This prevents a relative URI with a path
2332  *          such as "a:b/c/d" from later being re-parsed as an
2333  *          opaque URI with a scheme of "a" and a scheme-specific
2334  *          part of "b/c/d". (Deviation from RFC 2396)
2335  */
2336 void URI::normalize()
2338     std::vector<String> segments;
2340     //## Collect segments
2341     if (path.size()<2)
2342         return;
2343     bool abs = false;
2344     unsigned int pos=0;
2345     if (path[0]=='/')
2346         {
2347         abs = true;
2348         pos++;
2349         }
2350     while (pos < path.size())
2351         {
2352         unsigned int pos2 = path.find('/', pos);
2353         if (pos2==path.npos)
2354             {
2355             String seg = path.substr(pos);
2356             //printf("last segment:%s\n", seg.c_str());
2357             segments.push_back(seg);
2358             break;
2359             }
2360         if (pos2>pos)
2361             {
2362             String seg = path.substr(pos, pos2-pos);
2363             //printf("segment:%s\n", seg.c_str());
2364             segments.push_back(seg);
2365             }
2366         pos = pos2;
2367         pos++;
2368         }
2370     //## Clean up (normalize) segments
2371     bool edited = false;
2372     std::vector<String>::iterator iter;
2373     for (iter=segments.begin() ; iter!=segments.end() ; )
2374         {
2375         String s = *iter;
2376         if (s == ".")
2377             {
2378             iter = segments.erase(iter);
2379             edited = true;
2380             }
2381         else if (s == ".." &&
2382                  iter != segments.begin() &&
2383                  *(iter-1) != "..")
2384             {
2385             iter--; //back up, then erase two entries
2386             iter = segments.erase(iter);
2387             iter = segments.erase(iter);
2388             edited = true;
2389             }
2390         else
2391             iter++;
2392         }
2394     //## Rebuild path, if necessary
2395     if (edited)
2396         {
2397         path.clear();
2398         if (abs)
2399             {
2400             path.append("/");
2401             }
2402         std::vector<String>::iterator iter;
2403         for (iter=segments.begin() ; iter!=segments.end() ; iter++)
2404             {
2405             if (iter != segments.begin())
2406                 path.append("/");
2407             path.append(*iter);
2408             }
2409         }
2415 //#########################################################################
2416 //# M E S S A G E S
2417 //#########################################################################
2419 void URI::error(const char *fmt, ...)
2421     va_list args;
2422     fprintf(stderr, "URI error: ");
2423     va_start(args, fmt);
2424     vfprintf(stderr, fmt, args);
2425     va_end(args);
2426     fprintf(stderr, "\n");
2429 void URI::trace(const char *fmt, ...)
2431     va_list args;
2432     fprintf(stdout, "URI: ");
2433     va_start(args, fmt);
2434     vfprintf(stdout, fmt, args);
2435     va_end(args);
2436     fprintf(stdout, "\n");
2441 //#########################################################################
2442 //# P A R S I N G
2443 //#########################################################################
2447 int URI::peek(int p)
2449     if (p<0 || p>=parselen)
2450         return -1;
2451     return parsebuf[p];
2456 int URI::match(int p0, char *key)
2458     int p = p0;
2459     while (p < parselen)
2460         {
2461         if (*key == '\0')
2462             return p;
2463         else if (*key != parsebuf[p])
2464             break;
2465         p++; key++;
2466         }
2467     return p0;
2470 //#########################################################################
2471 //#  Parsing is performed according to:
2472 //#  http://www.gbiv.com/protocols/uri/rfc/rfc3986.html#components
2473 //#########################################################################
2475 int URI::parseScheme(int p0)
2477     int p = p0;
2478     for (LookupEntry *entry = schemes; entry->sval ; entry++)
2479         {
2480         int p2 = match(p, entry->sval);
2481         if (p2 > p)
2482             {
2483             schemeStr = entry->sval;
2484             scheme    = entry->ival;
2485             port      = entry->port;
2486             p = p2;
2487             return p;
2488             }
2489         }
2491     return p;
2495 int URI::parseHierarchicalPart(int p0)
2497     int p = p0;
2498     int ch;
2500     //# Authority field (host and port, for example)
2501     int p2 = match(p, "//");
2502     if (p2 > p)
2503         {
2504         p = p2;
2505         portSpecified = false;
2506         String portStr;
2507         while (p < parselen)
2508             {
2509             ch = peek(p);
2510             if (ch == '/')
2511                 break;
2512             else if (ch == ':')
2513                 portSpecified = true;
2514             else if (portSpecified)
2515                 portStr.push_back((XMLCh)ch);
2516             else
2517                 authority.push_back((XMLCh)ch);
2518             p++;
2519             }
2520         if (portStr.size() > 0)
2521             {
2522             char *pstr = (char *)portStr.c_str();
2523             char *endStr;
2524             long val = strtol(pstr, &endStr, 10);
2525             if (endStr > pstr) //successful parse?
2526                 port = val;
2527             }
2528         }
2530     //# Are we absolute?
2531     ch = peek(p);
2532     if (isLetter(ch) && peek(p+1)==':')
2533         {
2534         absolute = true;
2535         path.push_back((XMLCh)'/');
2536         }
2537     else if (ch == '/')
2538         {
2539         absolute = true;
2540         if (p>p0) //in other words, if '/' is not the first char
2541             opaque = true;
2542         path.push_back((XMLCh)ch);
2543         p++;
2544         }
2546     while (p < parselen)
2547         {
2548         ch = peek(p);
2549         if (ch == '?' || ch == '#')
2550             break;
2551         path.push_back((XMLCh)ch);
2552         p++;
2553         }
2555     return p;
2558 int URI::parseQuery(int p0)
2560     int p = p0;
2561     int ch = peek(p);
2562     if (ch != '?')
2563         return p0;
2565     p++;
2566     while (p < parselen)
2567         {
2568         ch = peek(p);
2569         if (ch == '#')
2570             break;
2571         query.push_back((XMLCh)ch);
2572         p++;
2573         }
2576     return p;
2579 int URI::parseFragment(int p0)
2582     int p = p0;
2583     int ch = peek(p);
2584     if (ch != '#')
2585         return p0;
2587     p++;
2588     while (p < parselen)
2589         {
2590         ch = peek(p);
2591         if (ch == '?')
2592             break;
2593         fragment.push_back((XMLCh)ch);
2594         p++;
2595         }
2598     return p;
2602 int URI::parse(int p0)
2605     int p = p0;
2607     int p2 = parseScheme(p);
2608     if (p2 < 0)
2609         {
2610         error("Scheme");
2611         return -1;
2612         }
2613     p = p2;
2616     p2 = parseHierarchicalPart(p);
2617     if (p2 < 0)
2618         {
2619         error("Hierarchical part");
2620         return -1;
2621         }
2622     p = p2;
2624     p2 = parseQuery(p);
2625     if (p2 < 0)
2626         {
2627         error("Query");
2628         return -1;
2629         }
2630     p = p2;
2633     p2 = parseFragment(p);
2634     if (p2 < 0)
2635         {
2636         error("Fragment");
2637         return -1;
2638         }
2639     p = p2;
2641     return p;
2647 bool URI::parse(const String &str)
2649     init();
2650     
2651     parselen = str.size();
2653     String tmp;
2654     for (unsigned int i=0 ; i<str.size() ; i++)
2655         {
2656         XMLCh ch = (XMLCh) str[i];
2657         if (ch == '\\')
2658             tmp.push_back((XMLCh)'/');
2659         else
2660             tmp.push_back(ch);
2661         }
2662     parsebuf = (char *) tmp.c_str();
2665     int p = parse(0);
2666     normalize();
2668     if (p < 0)
2669         {
2670         error("Syntax error");
2671         return false;
2672         }
2674     //printf("uri:%s\n", toString().c_str());
2675     //printf("path:%s\n", path.c_str());
2677     return true;
2688 //########################################################################
2689 //########################################################################
2690 //##  M A K E
2691 //########################################################################
2692 //########################################################################
2694 //########################################################################
2695 //# F I L E S E T
2696 //########################################################################
2697 /**
2698  * This is the descriptor for a <fileset> item
2699  */
2700 class FileSet
2702 public:
2704     /**
2705      *
2706      */
2707     FileSet()
2708         {}
2710     /**
2711      *
2712      */
2713     FileSet(const FileSet &other)
2714         { assign(other); }
2716     /**
2717      *
2718      */
2719     FileSet &operator=(const FileSet &other)
2720         { assign(other); return *this; }
2722     /**
2723      *
2724      */
2725     virtual ~FileSet()
2726         {}
2728     /**
2729      *
2730      */
2731     String getDirectory()
2732         { return directory; }
2733         
2734     /**
2735      *
2736      */
2737     void setDirectory(const String &val)
2738         { directory = val; }
2740     /**
2741      *
2742      */
2743     void setFiles(const std::vector<String> &val)
2744         { files = val; }
2746     /**
2747      *
2748      */
2749     std::vector<String> getFiles()
2750         { return files; }
2751         
2752     /**
2753      *
2754      */
2755     void setIncludes(const std::vector<String> &val)
2756         { includes = val; }
2758     /**
2759      *
2760      */
2761     std::vector<String> getIncludes()
2762         { return includes; }
2763         
2764     /**
2765      *
2766      */
2767     void setExcludes(const std::vector<String> &val)
2768         { excludes = val; }
2770     /**
2771      *
2772      */
2773     std::vector<String> getExcludes()
2774         { return excludes; }
2775         
2776     /**
2777      *
2778      */
2779     unsigned int size()
2780         { return files.size(); }
2781         
2782     /**
2783      *
2784      */
2785     String operator[](int index)
2786         { return files[index]; }
2787         
2788     /**
2789      *
2790      */
2791     void clear()
2792         {
2793         directory = "";
2794         files.clear();
2795         includes.clear();
2796         excludes.clear();
2797         }
2798         
2800 private:
2802     void assign(const FileSet &other)
2803         {
2804         directory = other.directory;
2805         files     = other.files;
2806         includes  = other.includes;
2807         excludes  = other.excludes;
2808         }
2810     String directory;
2811     std::vector<String> files;
2812     std::vector<String> includes;
2813     std::vector<String> excludes;
2814 };
2819 //########################################################################
2820 //# M A K E    B A S E
2821 //########################################################################
2822 /**
2823  * Base class for all classes in this file
2824  */
2825 class MakeBase
2827 public:
2828     MakeBase()
2829         {}
2830     virtual ~MakeBase()
2831         {}
2833     /**
2834      *     Return the URI of the file associated with this object 
2835      */     
2836     URI getURI()
2837         { return uri; }
2839     /**
2840      * Set the uri to the given string
2841      */
2842     void setURI(const String &uristr)
2843         { uri.parse(uristr); }
2845     /**
2846      *  Resolve another path relative to this one
2847      */
2848     String resolve(const String &otherPath);
2850     /**
2851      *  Get an element attribute, performing substitutions if necessary
2852      */
2853     bool getAttribute(Element *elem, const String &name, String &result);
2855     /**
2856      * Get an element value, performing substitutions if necessary
2857      */
2858     bool getValue(Element *elem, String &result);
2860 protected:
2862     /**
2863      *    The path to the file associated with this object
2864      */     
2865     URI uri;
2868     /**
2869      *  Print a printf()-like formatted error message
2870      */
2871     void error(char *fmt, ...);
2873     /**
2874      *  Print a printf()-like formatted trace message
2875      */
2876     void status(char *fmt, ...);
2878     /**
2879      *  Print a printf()-like formatted trace message
2880      */
2881     void trace(char *fmt, ...);
2883     /**
2884      *  Check if a given string matches a given regex pattern
2885      */
2886     bool regexMatch(const String &str, const String &pattern);
2888     /**
2889      *
2890      */
2891     String getSuffix(const String &fname);
2893     /**
2894      * Break up a string into substrings delimited the characters
2895      * in delimiters.  Null-length substrings are ignored
2896      */  
2897     std::vector<String> tokenize(const String &val,
2898                           const String &delimiters);
2900     /**
2901      *  replace runs of whitespace with a space
2902      */
2903     String strip(const String &s);
2905     /**
2906      *  remove leading whitespace from each line
2907      */
2908     String leftJustify(const String &s);
2910     /**
2911      *  remove leading and trailing whitespace from string
2912      */
2913     String trim(const String &s);
2915     /**
2916      * Return the native format of the canonical
2917      * path which we store
2918      */
2919     String getNativePath(const String &path);
2921     /**
2922      * Execute a shell command.  Outbuf is a ref to a string
2923      * to catch the result.     
2924      */         
2925     bool executeCommand(const String &call,
2926                         const String &inbuf,
2927                         String &outbuf,
2928                         String &errbuf);
2929     /**
2930      * List all directories in a given base and starting directory
2931      * It is usually called like:
2932      *        bool ret = listDirectories("src", "", result);    
2933      */         
2934     bool listDirectories(const String &baseName,
2935                          const String &dirname,
2936                          std::vector<String> &res);
2938     /**
2939      * Find all files in the named directory 
2940      */         
2941     bool listFiles(const String &baseName,
2942                    const String &dirname,
2943                    std::vector<String> &result);
2945     /**
2946      * Perform a listing for a fileset 
2947      */         
2948     bool listFiles(MakeBase &propRef, FileSet &fileSet);
2950     /**
2951      * Parse a <patternset>
2952      */  
2953     bool parsePatternSet(Element *elem,
2954                        MakeBase &propRef,
2955                        std::vector<String> &includes,
2956                        std::vector<String> &excludes);
2958     /**
2959      * Parse a <fileset> entry, and determine which files
2960      * should be included
2961      */  
2962     bool parseFileSet(Element *elem,
2963                     MakeBase &propRef,
2964                     FileSet &fileSet);
2966     /**
2967      * Return this object's property list
2968      */
2969     virtual std::map<String, String> &getProperties()
2970         { return properties; }
2972     /**
2973      * Return a named property if found, else a null string
2974      */
2975     virtual String getProperty(const String &name)
2976         {
2977         String val;
2978         std::map<String, String>::iterator iter;
2979         iter = properties.find(name);
2980         if (iter != properties.end())
2981             val = iter->second;
2982         return val;
2983         }
2986     std::map<String, String> properties;
2988     /**
2989      * Turn 'true' and 'false' into boolean values
2990      */             
2991     bool getBool(const String &str, bool &val);
2993     /**
2994      * Create a directory, making intermediate dirs
2995      * if necessary
2996      */                  
2997     bool createDirectory(const String &dirname);
2999     /**
3000      * Delete a directory and its children if desired
3001      */
3002     bool removeDirectory(const String &dirName);
3004     /**
3005      * Copy a file from one name to another. Perform only if needed
3006      */ 
3007     bool copyFile(const String &srcFile, const String &destFile);
3009     /**
3010      * Tests if the file exists and is a regular file
3011      */ 
3012     bool isRegularFile(const String &fileName);
3014     /**
3015      * Tests if the file exists and is a directory
3016      */ 
3017     bool isDirectory(const String &fileName);
3019     /**
3020      * Tests is the modification date of fileA is newer than fileB
3021      */ 
3022     bool isNewerThan(const String &fileA, const String &fileB);
3024 private:
3026     /**
3027      * replace variable refs like ${a} with their values
3028      */         
3029     bool getSubstitutions(const String &s, String &result);
3033 };
3038 /**
3039  *  Print a printf()-like formatted error message
3040  */
3041 void MakeBase::error(char *fmt, ...)
3043     va_list args;
3044     va_start(args,fmt);
3045     fprintf(stderr, "Make error: ");
3046     vfprintf(stderr, fmt, args);
3047     fprintf(stderr, "\n");
3048     va_end(args) ;
3053 /**
3054  *  Print a printf()-like formatted trace message
3055  */
3056 void MakeBase::status(char *fmt, ...)
3058     va_list args;
3059     va_start(args,fmt);
3060     //fprintf(stdout, " ");
3061     vfprintf(stdout, fmt, args);
3062     fprintf(stdout, "\n");
3063     va_end(args) ;
3068 /**
3069  *  Resolve another path relative to this one
3070  */
3071 String MakeBase::resolve(const String &otherPath)
3073     URI otherURI(otherPath);
3074     URI fullURI = uri.resolve(otherURI);
3075     String ret = fullURI.toString();
3076     return ret;
3080 /**
3081  *  Print a printf()-like formatted trace message
3082  */
3083 void MakeBase::trace(char *fmt, ...)
3085     va_list args;
3086     va_start(args,fmt);
3087     fprintf(stdout, "Make: ");
3088     vfprintf(stdout, fmt, args);
3089     fprintf(stdout, "\n");
3090     va_end(args) ;
3095 /**
3096  *  Check if a given string matches a given regex pattern
3097  */
3098 bool MakeBase::regexMatch(const String &str, const String &pattern)
3100     const TRexChar *terror = NULL;
3101     const TRexChar *cpat = pattern.c_str();
3102     TRex *expr = trex_compile(cpat, &terror);
3103     if (!expr)
3104         {
3105         if (!terror)
3106             terror = "undefined";
3107         error("compilation error [%s]!\n", terror);
3108         return false;
3109         } 
3111     bool ret = true;
3113     const TRexChar *cstr = str.c_str();
3114     if (trex_match(expr, cstr))
3115         {
3116         ret = true;
3117         }
3118     else
3119         {
3120         ret = false;
3121         }
3123     trex_free(expr);
3125     return ret;
3128 /**
3129  *  Return the suffix, if any, of a file name
3130  */
3131 String MakeBase::getSuffix(const String &fname)
3133     if (fname.size() < 2)
3134         return "";
3135     unsigned int pos = fname.find_last_of('.');
3136     if (pos == fname.npos)
3137         return "";
3138     pos++;
3139     String res = fname.substr(pos, fname.size()-pos);
3140     //trace("suffix:%s", res.c_str()); 
3141     return res;
3146 /**
3147  * Break up a string into substrings delimited the characters
3148  * in delimiters.  Null-length substrings are ignored
3149  */  
3150 std::vector<String> MakeBase::tokenize(const String &str,
3151                                 const String &delimiters)
3154     std::vector<String> res;
3155     char *del = (char *)delimiters.c_str();
3156     String dmp;
3157     for (unsigned int i=0 ; i<str.size() ; i++)
3158         {
3159         char ch = str[i];
3160         char *p = (char *)0;
3161         for (p=del ; *p ; p++)
3162             if (*p == ch)
3163                 break;
3164         if (*p)
3165             {
3166             if (dmp.size() > 0)
3167                 {
3168                 res.push_back(dmp);
3169                 dmp.clear();
3170                 }
3171             }
3172         else
3173             {
3174             dmp.push_back(ch);
3175             }
3176         }
3177     //Add tail
3178     if (dmp.size() > 0)
3179         {
3180         res.push_back(dmp);
3181         dmp.clear();
3182         }
3184     return res;
3189 /**
3190  *  replace runs of whitespace with a single space
3191  */
3192 String MakeBase::strip(const String &s)
3194     int len = s.size();
3195     String stripped;
3196     for (int i = 0 ; i<len ; i++)
3197         {
3198         char ch = s[i];
3199         if (isspace(ch))
3200             {
3201             stripped.push_back(' ');
3202             for ( ; i<len ; i++)
3203                 {
3204                 ch = s[i];
3205                 if (!isspace(ch))
3206                     {
3207                     stripped.push_back(ch);
3208                     break;
3209                     }
3210                 }
3211             }
3212         else
3213             {
3214             stripped.push_back(ch);
3215             }
3216         }
3217     return stripped;
3220 /**
3221  *  remove leading whitespace from each line
3222  */
3223 String MakeBase::leftJustify(const String &s)
3225     String out;
3226     int len = s.size();
3227     for (int i = 0 ; i<len ; )
3228         {
3229         char ch;
3230         //Skip to first visible character
3231         while (i<len)
3232             {
3233             ch = s[i];
3234             if (ch == '\n' || ch == '\r'
3235               || !isspace(ch))
3236                   break;
3237             i++;
3238             }
3239         //Copy the rest of the line
3240         while (i<len)
3241             {
3242             ch = s[i];
3243             if (ch == '\n' || ch == '\r')
3244                 {
3245                 if (ch != '\r')
3246                     out.push_back('\n');
3247                 i++;
3248                 break;
3249                 }
3250             else
3251                 {
3252                 out.push_back(ch);
3253                 }
3254             i++;
3255             }
3256         }
3257     return out;
3261 /**
3262  *  Removes whitespace from beginning and end of a string
3263  */
3264 String MakeBase::trim(const String &s)
3266     if (s.size() < 1)
3267         return s;
3268     
3269     //Find first non-ws char
3270     unsigned int begin = 0;
3271     for ( ; begin < s.size() ; begin++)
3272         {
3273         if (!isspace(s[begin]))
3274             break;
3275         }
3277     //Find first non-ws char, going in reverse
3278     unsigned int end = s.size() - 1;
3279     for ( ; end > begin ; end--)
3280         {
3281         if (!isspace(s[end]))
3282             break;
3283         }
3284     //trace("begin:%d  end:%d", begin, end);
3286     String res = s.substr(begin, end-begin+1);
3287     return res;
3290 /**
3291  * Return the native format of the canonical
3292  * path which we store
3293  */
3294 String MakeBase::getNativePath(const String &path)
3296 #ifdef __WIN32__
3297     String npath;
3298     unsigned int firstChar = 0;
3299     if (path.size() >= 3)
3300         {
3301         if (path[0] == '/' &&
3302             isalpha(path[1]) &&
3303             path[2] == ':')
3304             firstChar++;
3305         }
3306     for (unsigned int i=firstChar ; i<path.size() ; i++)
3307         {
3308         char ch = path[i];
3309         if (ch == '/')
3310             npath.push_back('\\');
3311         else
3312             npath.push_back(ch);
3313         }
3314     return npath;
3315 #else
3316     return path;
3317 #endif
3321 #ifdef __WIN32__
3322 #include <tchar.h>
3324 static String win32LastError()
3327     DWORD dw = GetLastError(); 
3329     LPVOID str;
3330     FormatMessage(
3331         FORMAT_MESSAGE_ALLOCATE_BUFFER | 
3332         FORMAT_MESSAGE_FROM_SYSTEM,
3333         NULL,
3334         dw,
3335         0,
3336         (LPTSTR) &str,
3337         0, NULL );
3338     LPTSTR p = _tcschr((const char *)str, _T('\r'));
3339     if(p != NULL)
3340         { // lose CRLF
3341         *p = _T('\0');
3342         }
3343     String ret = (char *)str;
3344     LocalFree(str);
3346     return ret;
3348 #endif
3352 /**
3353  * Execute a system call, using pipes to send data to the
3354  * program's stdin,  and reading stdout and stderr.
3355  */
3356 bool MakeBase::executeCommand(const String &command,
3357                               const String &inbuf,
3358                               String &outbuf,
3359                               String &errbuf)
3362     status("============ cmd ============\n%s\n=============================",
3363                 command.c_str());
3365     outbuf.clear();
3366     errbuf.clear();
3367     
3368 #ifdef __WIN32__
3370     /*
3371     I really hate having win32 code in this program, but the
3372     read buffer in command.com and cmd.exe are just too small
3373     for the large commands we need for compiling and linking.
3374     */
3376     bool ret = true;
3378     //# Allocate a separate buffer for safety
3379     char *paramBuf = new char[command.size() + 1];
3380     if (!paramBuf)
3381        {
3382        error("executeCommand cannot allocate command buffer");
3383        return false;
3384        }
3385     strcpy(paramBuf, (char *)command.c_str());
3387     //# Create pipes
3388     SECURITY_ATTRIBUTES saAttr; 
3389     saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); 
3390     saAttr.bInheritHandle = TRUE; 
3391     saAttr.lpSecurityDescriptor = NULL; 
3392     HANDLE stdinRead,  stdinWrite;
3393     HANDLE stdoutRead, stdoutWrite;
3394     HANDLE stderrRead, stderrWrite;
3395     if (!CreatePipe(&stdinRead, &stdinWrite, &saAttr, 0))
3396         {
3397         error("executeProgram: could not create pipe");
3398         delete[] paramBuf;
3399         return false;
3400         } 
3401     SetHandleInformation(stdinWrite, HANDLE_FLAG_INHERIT, 0);
3402     if (!CreatePipe(&stdoutRead, &stdoutWrite, &saAttr, 0))
3403         {
3404         error("executeProgram: could not create pipe");
3405         delete[] paramBuf;
3406         return false;
3407         } 
3408     SetHandleInformation(stdoutRead, HANDLE_FLAG_INHERIT, 0);
3409     if (!CreatePipe(&stderrRead, &stderrWrite, &saAttr, 0))
3410         {
3411         error("executeProgram: could not create pipe");
3412         delete[] paramBuf;
3413         return false;
3414         } 
3415     SetHandleInformation(stderrRead, HANDLE_FLAG_INHERIT, 0);
3417     // Create the process
3418     STARTUPINFO siStartupInfo;
3419     PROCESS_INFORMATION piProcessInfo;
3420     memset(&siStartupInfo, 0, sizeof(siStartupInfo));
3421     memset(&piProcessInfo, 0, sizeof(piProcessInfo));
3422     siStartupInfo.cb = sizeof(siStartupInfo);
3423     siStartupInfo.hStdError   =  stderrWrite;
3424     siStartupInfo.hStdOutput  =  stdoutWrite;
3425     siStartupInfo.hStdInput   =  stdinRead;
3426     siStartupInfo.dwFlags    |=  STARTF_USESTDHANDLES;
3427    
3428     if (!CreateProcess(NULL, paramBuf, NULL, NULL, true,
3429                 0, NULL, NULL, &siStartupInfo,
3430                 &piProcessInfo))
3431         {
3432         error("executeCommand : could not create process : %s",
3433                     win32LastError().c_str());
3434         ret = false;
3435         }
3437     DWORD bytesWritten;
3438     if (inbuf.size()>0 &&
3439         !WriteFile(stdinWrite, inbuf.c_str(), inbuf.size(), 
3440                &bytesWritten, NULL))
3441         {
3442         error("executeCommand: could not write to pipe");
3443         return false;
3444         }    
3445     if (!CloseHandle(stdinWrite))
3446         {          
3447         error("executeCommand: could not close write pipe");
3448         return false;
3449         }
3450     if (!CloseHandle(stdoutWrite))
3451         {
3452         error("executeCommand: could not close read pipe");
3453         return false;
3454         }
3455     if (!CloseHandle(stderrWrite))
3456         {
3457         error("executeCommand: could not close read pipe");
3458         return false;
3459         }
3460     while (true)
3461         {
3462         //trace("## stderr");
3463         DWORD avail;
3464         if (!PeekNamedPipe(stderrRead, NULL, 0, NULL, &avail, NULL))
3465             break;
3466         if (avail > 0)
3467             {
3468             DWORD bytesRead = 0;
3469             char readBuf[1025];
3470             if (avail>1024) avail = 1024;
3471             if (!ReadFile(stderrRead, readBuf, avail, &bytesRead, NULL)
3472                 || bytesRead == 0)
3473                 {
3474                 break;
3475                 }
3476             for (unsigned int i=0 ; i<bytesRead ; i++)
3477                 errbuf.push_back(readBuf[i]);
3478             }
3479         //trace("## stdout");
3480         if (!PeekNamedPipe(stdoutRead, NULL, 0, NULL, &avail, NULL))
3481             break;
3482         if (avail > 0)
3483             {
3484             DWORD bytesRead = 0;
3485             char readBuf[1025];
3486             if (avail>1024) avail = 1024;
3487             if (!ReadFile(stdoutRead, readBuf, avail, &bytesRead, NULL)
3488                 || bytesRead==0)
3489                 {
3490                 break;
3491                 }
3492             for (unsigned int i=0 ; i<bytesRead ; i++)
3493                 outbuf.push_back(readBuf[i]);
3494             }
3495         DWORD exitCode;
3496         GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
3497         if (exitCode != STILL_ACTIVE)
3498             break;
3499         Sleep(100);
3500         }    
3501     //trace("outbuf:%s", outbuf.c_str());
3502     if (!CloseHandle(stdoutRead))
3503         {
3504         error("executeCommand: could not close read pipe");
3505         return false;
3506         }
3507     if (!CloseHandle(stderrRead))
3508         {
3509         error("executeCommand: could not close read pipe");
3510         return false;
3511         }
3513     DWORD exitCode;
3514     GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
3515     //trace("exit code:%d", exitCode);
3516     if (exitCode != 0)
3517         {
3518         ret = false;
3519         }
3520     
3521     // Clean up
3522     CloseHandle(piProcessInfo.hProcess);
3523     CloseHandle(piProcessInfo.hThread);
3526     return ret;
3528 #else //do it unix-style
3530     String s;
3531     FILE *f = popen(command.c_str(), "r");
3532     int errnum = 0;
3533     if (f)
3534         {
3535         while (true)
3536             {
3537             int ch = fgetc(f);
3538             if (ch < 0)
3539                 break;
3540             s.push_back((char)ch);
3541             }
3542         errnum = pclose(f);
3543         }
3544     outbuf = s;
3545     if (errnum != 0)
3546         {
3547         error("exec of command '%s' failed : %s",
3548              command.c_str(), strerror(errno));
3549         return false;
3550         }
3551     else
3552         return true;
3554 #endif
3555
3560 bool MakeBase::listDirectories(const String &baseName,
3561                               const String &dirName,
3562                               std::vector<String> &res)
3564     res.push_back(dirName);
3565     String fullPath = baseName;
3566     if (dirName.size()>0)
3567         {
3568         fullPath.append("/");
3569         fullPath.append(dirName);
3570         }
3571     DIR *dir = opendir(fullPath.c_str());
3572     while (true)
3573         {
3574         struct dirent *de = readdir(dir);
3575         if (!de)
3576             break;
3578         //Get the directory member name
3579         String s = de->d_name;
3580         if (s.size() == 0 || s[0] == '.')
3581             continue;
3582         String childName = dirName;
3583         childName.append("/");
3584         childName.append(s);
3586         String fullChildPath = baseName;
3587         fullChildPath.append("/");
3588         fullChildPath.append(childName);
3589         struct stat finfo;
3590         String childNative = getNativePath(fullChildPath);
3591         if (stat(childNative.c_str(), &finfo)<0)
3592             {
3593             error("cannot stat file:%s", childNative.c_str());
3594             }
3595         else if (S_ISDIR(finfo.st_mode))
3596             {
3597             //trace("directory: %s", childName.c_str());
3598             if (!listDirectories(baseName, childName, res))
3599                 return false;
3600             }
3601         }
3602     closedir(dir);
3604     return true;
3608 bool MakeBase::listFiles(const String &baseDir,
3609                          const String &dirName,
3610                          std::vector<String> &res)
3612     String fullDir = baseDir;
3613     if (dirName.size()>0)
3614         {
3615         fullDir.append("/");
3616         fullDir.append(dirName);
3617         }
3618     String dirNative = getNativePath(fullDir);
3620     std::vector<String> subdirs;
3621     DIR *dir = opendir(dirNative.c_str());
3622     if (!dir)
3623         {
3624         error("Could not open directory %s : %s",
3625               dirNative.c_str(), strerror(errno));
3626         return false;
3627         }
3628     while (true)
3629         {
3630         struct dirent *de = readdir(dir);
3631         if (!de)
3632             break;
3634         //Get the directory member name
3635         String s = de->d_name;
3636         if (s.size() == 0 || s[0] == '.')
3637             continue;
3638         String childName;
3639         if (dirName.size()>0)
3640             {
3641             childName.append(dirName);
3642             childName.append("/");
3643             }
3644         childName.append(s);
3645         String fullChild = baseDir;
3646         fullChild.append("/");
3647         fullChild.append(childName);
3648         
3649         if (isDirectory(fullChild))
3650             {
3651             //trace("directory: %s", childName.c_str());
3652             if (!listFiles(baseDir, childName, res))
3653                 return false;
3654             continue;
3655             }
3656         else if (!isRegularFile(fullChild))
3657             {
3658             error("unknown file:%s", childName.c_str());
3659             return false;
3660             }
3662        //all done!
3663         res.push_back(childName);
3665         }
3666     closedir(dir);
3668     return true;
3672 bool MakeBase::listFiles(MakeBase &propRef, FileSet &fileSet)
3674     String baseDir = propRef.resolve(fileSet.getDirectory());
3675     std::vector<String> fileList;
3676     if (!listFiles(baseDir, "", fileList))
3677         return false;
3679     std::vector<String> includes = fileSet.getIncludes();
3680     std::vector<String> excludes = fileSet.getExcludes();
3682     std::vector<String> incs;
3683     std::vector<String>::iterator iter;
3685     std::sort(fileList.begin(), fileList.end());
3687     //If there are <includes>, then add files to the output
3688     //in the order of the include list
3689     if (includes.size()==0)
3690         incs = fileList;
3691     else
3692         {
3693         for (iter = includes.begin() ; iter != includes.end() ; iter++)
3694             {
3695             String pattern = *iter;
3696             std::vector<String>::iterator siter;
3697             for (siter = fileList.begin() ; siter != fileList.end() ; siter++)
3698                 {
3699                 String s = *siter;
3700                 if (regexMatch(s, pattern))
3701                     {
3702                     //trace("INCLUDED:%s", s.c_str());
3703                     incs.push_back(s);
3704                     }
3705                 }
3706             }
3707         }
3709     //Now trim off the <excludes>
3710     std::vector<String> res;
3711     for (iter = incs.begin() ; iter != incs.end() ; iter++)
3712         {
3713         String s = *iter;
3714         bool skipme = false;
3715         std::vector<String>::iterator siter;
3716         for (siter = excludes.begin() ; siter != excludes.end() ; siter++)
3717             {
3718             String pattern = *siter;
3719             if (regexMatch(s, pattern))
3720                 {
3721                 //trace("EXCLUDED:%s", s.c_str());
3722                 skipme = true;
3723                 break;
3724                 }
3725             }
3726         if (!skipme)
3727             res.push_back(s);
3728         }
3729         
3730     fileSet.setFiles(res);
3732     return true;
3739 bool MakeBase::getSubstitutions(const String &str, String &result)
3741     String s = trim(str);
3742     int len = (int)s.size();
3743     String val;
3744     for (int i=0 ; i<len ; i++)
3745         {
3746         char ch = s[i];
3747         if (ch == '$' && s[i+1] == '{')
3748             {
3749             String varname;
3750             int j = i+2;
3751             for ( ; j<len ; j++)
3752                 {
3753                 ch = s[j];
3754                 if (ch == '$' && s[j+1] == '{')
3755                     {
3756                     error("attribute %s cannot have nested variable references",
3757                            s.c_str());
3758                     return false;
3759                     }
3760                 else if (ch == '}')
3761                     {
3762                     std::map<String, String>::iterator iter;
3763                     iter = properties.find(trim(varname));
3764                     if (iter != properties.end())
3765                         {
3766                         val.append(iter->second);
3767                         }
3768                     else
3769                         {
3770                         error("property ${%s} not found", varname.c_str());
3771                         return false;
3772                         }
3773                     break;
3774                     }
3775                 else
3776                     {
3777                     varname.push_back(ch);
3778                     }
3779                 }
3780             i = j;
3781             }
3782         else
3783             {
3784             val.push_back(ch);
3785             }
3786         }
3787     result = val;
3788     return true;
3792 bool MakeBase::getAttribute(Element *elem, const String &name,
3793                                     String &result)
3795     String s = elem->getAttribute(name);
3796     return getSubstitutions(s, result);
3800 bool MakeBase::getValue(Element *elem, String &result)
3802     String s = elem->getValue();
3803     //Replace all runs of whitespace with a single space
3804     return getSubstitutions(s, result);
3808 /**
3809  * Turn 'true' and 'false' into boolean values
3810  */             
3811 bool MakeBase::getBool(const String &str, bool &val)
3813     if (str == "true")
3814         val = true;
3815     else if (str == "false")
3816         val = false;
3817     else
3818         {
3819         error("expected 'true' or 'false'.  found '%s'", str.c_str());
3820         return false;
3821         }
3822     return true;
3828 /**
3829  * Parse a <patternset> entry
3830  */  
3831 bool MakeBase::parsePatternSet(Element *elem,
3832                           MakeBase &propRef,
3833                           std::vector<String> &includes,
3834                           std::vector<String> &excludes
3835                           )
3837     std::vector<Element *> children  = elem->getChildren();
3838     for (unsigned int i=0 ; i<children.size() ; i++)
3839         {
3840         Element *child = children[i];
3841         String tagName = child->getName();
3842         if (tagName == "exclude")
3843             {
3844             String fname;
3845             if (!propRef.getAttribute(child, "name", fname))
3846                 return false;
3847             //trace("EXCLUDE: %s", fname.c_str());
3848             excludes.push_back(fname);
3849             }
3850         else if (tagName == "include")
3851             {
3852             String fname;
3853             if (!propRef.getAttribute(child, "name", fname))
3854                 return false;
3855             //trace("INCLUDE: %s", fname.c_str());
3856             includes.push_back(fname);
3857             }
3858         }
3860     return true;
3866 /**
3867  * Parse a <fileset> entry, and determine which files
3868  * should be included
3869  */  
3870 bool MakeBase::parseFileSet(Element *elem,
3871                           MakeBase &propRef,
3872                           FileSet &fileSet)
3874     String name = elem->getName();
3875     if (name != "fileset")
3876         {
3877         error("expected <fileset>");
3878         return false;
3879         }
3882     std::vector<String> includes;
3883     std::vector<String> excludes;
3885     //A fileset has one implied patternset
3886     if (!parsePatternSet(elem, propRef, includes, excludes))
3887         {
3888         return false;
3889         }
3890     //Look for child tags, including more patternsets
3891     std::vector<Element *> children  = elem->getChildren();
3892     for (unsigned int i=0 ; i<children.size() ; i++)
3893         {
3894         Element *child = children[i];
3895         String tagName = child->getName();
3896         if (tagName == "patternset")
3897             {
3898             if (!parsePatternSet(child, propRef, includes, excludes))
3899                 {
3900                 return false;
3901                 }
3902             }
3903         }
3905     String dir;
3906     //Now do the stuff
3907     //Get the base directory for reading file names
3908     if (!propRef.getAttribute(elem, "dir", dir))
3909         return false;
3911     fileSet.setDirectory(dir);
3912     fileSet.setIncludes(includes);
3913     fileSet.setExcludes(excludes);
3914     
3915     /*
3916     std::vector<String> fileList;
3917     if (dir.size() > 0)
3918         {
3919         String baseDir = propRef.resolve(dir);
3920         if (!listFiles(baseDir, "", includes, excludes, fileList))
3921             return false;
3922         }
3923     std::sort(fileList.begin(), fileList.end());
3924     result = fileList;
3925     */
3927     
3928     /*
3929     for (unsigned int i=0 ; i<result.size() ; i++)
3930         {
3931         trace("RES:%s", result[i].c_str());
3932         }
3933     */
3935     
3936     return true;
3941 /**
3942  * Create a directory, making intermediate dirs
3943  * if necessary
3944  */                  
3945 bool MakeBase::createDirectory(const String &dirname)
3947     //trace("## createDirectory: %s", dirname.c_str());
3948     //## first check if it exists
3949     struct stat finfo;
3950     String nativeDir = getNativePath(dirname);
3951     char *cnative = (char *) nativeDir.c_str();
3952 #ifdef __WIN32__
3953     if (strlen(cnative)==2 && cnative[1]==':')
3954         return true;
3955 #endif
3956     if (stat(cnative, &finfo)==0)
3957         {
3958         if (!S_ISDIR(finfo.st_mode))
3959             {
3960             error("mkdir: file %s exists but is not a directory",
3961                   cnative);
3962             return false;
3963             }
3964         else //exists
3965             {
3966             return true;
3967             }
3968         }
3970     //## 2: pull off the last path segment, if any,
3971     //## to make the dir 'above' this one, if necessary
3972     unsigned int pos = dirname.find_last_of('/');
3973     if (pos>0 && pos != dirname.npos)
3974         {
3975         String subpath = dirname.substr(0, pos);
3976         //A letter root (c:) ?
3977         if (!createDirectory(subpath))
3978             return false;
3979         }
3980         
3981     //## 3: now make
3982 #ifdef __WIN32__
3983     if (mkdir(cnative)<0)
3984 #else
3985     if (mkdir(cnative, S_IRWXU | S_IRWXG | S_IRWXO)<0)
3986 #endif
3987         {
3988         error("cannot make directory '%s' : %s",
3989                  cnative, strerror(errno));
3990         return false;
3991         }
3992         
3993     return true;
3997 /**
3998  * Remove a directory recursively
3999  */ 
4000 bool MakeBase::removeDirectory(const String &dirName)
4002     char *dname = (char *)dirName.c_str();
4004     DIR *dir = opendir(dname);
4005     if (!dir)
4006         {
4007         //# Let this fail nicely.
4008         return true;
4009         //error("error opening directory %s : %s", dname, strerror(errno));
4010         //return false;
4011         }
4012     
4013     while (true)
4014         {
4015         struct dirent *de = readdir(dir);
4016         if (!de)
4017             break;
4019         //Get the directory member name
4020         String s = de->d_name;
4021         if (s.size() == 0 || s[0] == '.')
4022             continue;
4023         String childName;
4024         if (dirName.size() > 0)
4025             {
4026             childName.append(dirName);
4027             childName.append("/");
4028             }
4029         childName.append(s);
4032         struct stat finfo;
4033         String childNative = getNativePath(childName);
4034         char *cnative = (char *)childNative.c_str();
4035         if (stat(cnative, &finfo)<0)
4036             {
4037             error("cannot stat file:%s", cnative);
4038             }
4039         else if (S_ISDIR(finfo.st_mode))
4040             {
4041             //trace("DEL dir: %s", childName.c_str());
4042             if (!removeDirectory(childName))
4043                 {
4044                 return false;
4045                 }
4046             }
4047         else if (!S_ISREG(finfo.st_mode))
4048             {
4049             //trace("not regular: %s", cnative);
4050             }
4051         else
4052             {
4053             //trace("DEL file: %s", childName.c_str());
4054             if (remove(cnative)<0)
4055                 {
4056                 error("error deleting %s : %s",
4057                      cnative, strerror(errno));
4058                 return false;
4059                 }
4060             }
4061         }
4062     closedir(dir);
4064     //Now delete the directory
4065     String native = getNativePath(dirName);
4066     if (rmdir(native.c_str())<0)
4067         {
4068         error("could not delete directory %s : %s",
4069             native.c_str() , strerror(errno));
4070         return false;
4071         }
4073     return true;
4074     
4078 /**
4079  * Copy a file from one name to another. Perform only if needed
4080  */ 
4081 bool MakeBase::copyFile(const String &srcFile, const String &destFile)
4083     //# 1 Check up-to-date times
4084     String srcNative = getNativePath(srcFile);
4085     struct stat srcinfo;
4086     if (stat(srcNative.c_str(), &srcinfo)<0)
4087         {
4088         error("source file %s for copy does not exist",
4089                  srcNative.c_str());
4090         return false;
4091         }
4093     String destNative = getNativePath(destFile);
4094     struct stat destinfo;
4095     if (stat(destNative.c_str(), &destinfo)==0)
4096         {
4097         if (destinfo.st_mtime >= srcinfo.st_mtime)
4098             return true;
4099         }
4100         
4101     //# 2 prepare a destination directory if necessary
4102     unsigned int pos = destFile.find_last_of('/');
4103     if (pos != destFile.npos)
4104         {
4105         String subpath = destFile.substr(0, pos);
4106         if (!createDirectory(subpath))
4107             return false;
4108         }
4110     //# 3 do the data copy
4111 #ifndef __WIN32__
4113     FILE *srcf = fopen(srcNative.c_str(), "rb");
4114     if (!srcf)
4115         {
4116         error("copyFile cannot open '%s' for reading", srcNative.c_str());
4117         return false;
4118         }
4119     FILE *destf = fopen(destNative.c_str(), "wb");
4120     if (!destf)
4121         {
4122         error("copyFile cannot open %s for writing", srcNative.c_str());
4123         return false;
4124         }
4126     while (!feof(srcf))
4127         {
4128         int ch = fgetc(srcf);
4129         if (ch<0)
4130             break;
4131         fputc(ch, destf);
4132         }
4134     fclose(destf);
4135     fclose(srcf);
4137 #else
4138     
4139     if (!CopyFile(srcNative.c_str(), destNative.c_str(), false))
4140         {
4141         error("copyFile from %s to %s failed",
4142              srcNative.c_str(), destNative.c_str());
4143         return false;
4144         }
4145         
4146 #endif /* __WIN32__ */
4149     return true;
4154 /**
4155  * Tests if the file exists and is a regular file
4156  */ 
4157 bool MakeBase::isRegularFile(const String &fileName)
4159     String native = getNativePath(fileName);
4160     struct stat finfo;
4161     
4162     //Exists?
4163     if (stat(native.c_str(), &finfo)<0)
4164         return false;
4167     //check the file mode
4168     if (!S_ISREG(finfo.st_mode))
4169         return false;
4171     return true;
4174 /**
4175  * Tests if the file exists and is a directory
4176  */ 
4177 bool MakeBase::isDirectory(const String &fileName)
4179     String native = getNativePath(fileName);
4180     struct stat finfo;
4181     
4182     //Exists?
4183     if (stat(native.c_str(), &finfo)<0)
4184         return false;
4187     //check the file mode
4188     if (!S_ISDIR(finfo.st_mode))
4189         return false;
4191     return true;
4196 /**
4197  * Tests is the modification of fileA is newer than fileB
4198  */ 
4199 bool MakeBase::isNewerThan(const String &fileA, const String &fileB)
4201     //trace("isNewerThan:'%s' , '%s'", fileA.c_str(), fileB.c_str());
4202     String nativeA = getNativePath(fileA);
4203     struct stat infoA;
4204     //IF source does not exist, NOT newer
4205     if (stat(nativeA.c_str(), &infoA)<0)
4206         {
4207         return false;
4208         }
4210     String nativeB = getNativePath(fileB);
4211     struct stat infoB;
4212     //IF dest does not exist, YES, newer
4213     if (stat(nativeB.c_str(), &infoB)<0)
4214         {
4215         return true;
4216         }
4218     //check the actual times
4219     if (infoA.st_mtime > infoB.st_mtime)
4220         {
4221         return true;
4222         }
4224     return false;
4228 //########################################################################
4229 //# P K G    C O N F I G
4230 //########################################################################
4232 /**
4233  *
4234  */
4235 class PkgConfig : public MakeBase
4238 public:
4240     /**
4241      *
4242      */
4243     PkgConfig()
4244         { init(); }
4246     /**
4247      *
4248      */
4249     PkgConfig(const String &namearg)
4250         { init(); name = namearg; }
4252     /**
4253      *
4254      */
4255     PkgConfig(const PkgConfig &other)
4256         { assign(other); }
4258     /**
4259      *
4260      */
4261     PkgConfig &operator=(const PkgConfig &other)
4262         { assign(other); return *this; }
4264     /**
4265      *
4266      */
4267     virtual ~PkgConfig()
4268         { }
4270     /**
4271      *
4272      */
4273     virtual String getName()
4274         { return name; }
4276     /**
4277      *
4278      */
4279     virtual String getDescription()
4280         { return description; }
4282     /**
4283      *
4284      */
4285     virtual String getCflags()
4286         { return cflags; }
4288     /**
4289      *
4290      */
4291     virtual String getLibs()
4292         { return libs; }
4294     /**
4295      *
4296      */
4297     virtual String getVersion()
4298         { return version; }
4300     /**
4301      *
4302      */
4303     virtual int getMajorVersion()
4304         { return majorVersion; }
4306     /**
4307      *
4308      */
4309     virtual int getMinorVersion()
4310         { return minorVersion; }
4312     /**
4313      *
4314      */
4315     virtual int getMicroVersion()
4316         { return microVersion; }
4318     /**
4319      *
4320      */
4321     virtual std::map<String, String> &getAttributes()
4322         { return attrs; }
4324     /**
4325      *
4326      */
4327     virtual std::vector<String> &getRequireList()
4328         { return requireList; }
4330     virtual bool readFile(const String &fileName);
4332 private:
4334     void init()
4335         {
4336         name         = "";
4337         description  = "";
4338         cflags       = "";
4339         libs         = "";
4340         requires     = "";
4341         version      = "";
4342         majorVersion = 0;
4343         minorVersion = 0;
4344         microVersion = 0;
4345         fileName     = "";
4346         attrs.clear();
4347         requireList.clear();
4348         }
4350     void assign(const PkgConfig &other)
4351         {
4352         name         = other.name;
4353         description  = other.description;
4354         cflags       = other.cflags;
4355         libs         = other.libs;
4356         requires     = other.requires;
4357         version      = other.version;
4358         majorVersion = other.majorVersion;
4359         minorVersion = other.minorVersion;
4360         microVersion = other.microVersion;
4361         fileName     = other.fileName;
4362         attrs        = other.attrs;
4363         requireList  = other.requireList;
4364         }
4368     int get(int pos);
4370     int skipwhite(int pos);
4372     int getword(int pos, String &ret);
4374     void parseRequires();
4376     void parseVersion();
4378     bool parse(const String &buf);
4380     void dumpAttrs();
4382     String name;
4384     String description;
4386     String cflags;
4388     String libs;
4390     String requires;
4392     String version;
4394     int majorVersion;
4396     int minorVersion;
4398     int microVersion;
4400     String fileName;
4402     std::map<String, String> attrs;
4404     std::vector<String> requireList;
4406     char *parsebuf;
4407     int parselen;
4408 };
4411 /**
4412  * Get a character from the buffer at pos.  If out of range,
4413  * return -1 for safety
4414  */
4415 int PkgConfig::get(int pos)
4417     if (pos>parselen)
4418         return -1;
4419     return parsebuf[pos];
4424 /**
4425  *  Skip over all whitespace characters beginning at pos.  Return
4426  *  the position of the first non-whitespace character.
4427  */
4428 int PkgConfig::skipwhite(int pos)
4430     while (pos < parselen)
4431         {
4432         int ch = get(pos);
4433         if (ch < 0)
4434             break;
4435         if (!isspace(ch))
4436             break;
4437         pos++;
4438         }
4439     return pos;
4443 /**
4444  *  Parse the buffer beginning at pos, for a word.  Fill
4445  *  'ret' with the result.  Return the position after the
4446  *  word.
4447  */
4448 int PkgConfig::getword(int pos, String &ret)
4450     while (pos < parselen)
4451         {
4452         int ch = get(pos);
4453         if (ch < 0)
4454             break;
4455         if (!isalnum(ch) && ch != '_' && ch != '-'&& ch != '.')
4456             break;
4457         ret.push_back((char)ch);
4458         pos++;
4459         }
4460     return pos;
4463 void PkgConfig::parseRequires()
4465     if (requires.size() == 0)
4466         return;
4467     parsebuf = (char *)requires.c_str();
4468     parselen = requires.size();
4469     int pos = 0;
4470     while (pos < parselen)
4471         {
4472         pos = skipwhite(pos);
4473         String val;
4474         int pos2 = getword(pos, val);
4475         if (pos2 == pos)
4476             break;
4477         pos = pos2;
4478         //trace("val %s", val.c_str());
4479         requireList.push_back(val);
4480         }
4483 static int getint(const String str)
4485     char *s = (char *)str.c_str();
4486     char *ends = NULL;
4487     long val = strtol(s, &ends, 10);
4488     if (ends == s)
4489         return 0L;
4490     else
4491         return val;
4494 void PkgConfig::parseVersion()
4496     if (version.size() == 0)
4497         return;
4498     String s1, s2, s3;
4499     unsigned int pos = 0;
4500     unsigned int pos2 = version.find('.', pos);
4501     if (pos2 == version.npos)
4502         {
4503         s1 = version;
4504         }
4505     else
4506         {
4507         s1 = version.substr(pos, pos2-pos);
4508         pos = pos2;
4509         pos++;
4510         if (pos < version.size())
4511             {
4512             pos2 = version.find('.', pos);
4513             if (pos2 == version.npos)
4514                 {
4515                 s2 = version.substr(pos, version.size()-pos);
4516                 }
4517             else
4518                 {
4519                 s2 = version.substr(pos, pos2-pos);
4520                 pos = pos2;
4521                 pos++;
4522                 if (pos < version.size())
4523                     s3 = version.substr(pos, pos2-pos);
4524                 }
4525             }
4526         }
4528     majorVersion = getint(s1);
4529     minorVersion = getint(s2);
4530     microVersion = getint(s3);
4531     //trace("version:%d.%d.%d", majorVersion,
4532     //          minorVersion, microVersion );
4536 bool PkgConfig::parse(const String &buf)
4538     init();
4540     parsebuf = (char *)buf.c_str();
4541     parselen = buf.size();
4542     int pos = 0;
4545     while (pos < parselen)
4546         {
4547         String attrName;
4548         pos = skipwhite(pos);
4549         int ch = get(pos);
4550         if (ch == '#')
4551             {
4552             //comment.  eat the rest of the line
4553             while (pos < parselen)
4554                 {
4555                 ch = get(pos);
4556                 if (ch == '\n' || ch < 0)
4557                     break;
4558                 pos++;
4559                 }
4560             continue;
4561             }
4562         pos = getword(pos, attrName);
4563         if (attrName.size() == 0)
4564             continue;
4565         pos = skipwhite(pos);
4566         ch = get(pos);
4567         if (ch != ':' && ch != '=')
4568             {
4569             error("expected ':' or '='");
4570             return false;
4571             }
4572         pos++;
4573         pos = skipwhite(pos);
4574         String attrVal;
4575         while (pos < parselen)
4576             {
4577             ch = get(pos);
4578             if (ch == '\n' || ch < 0)
4579                 break;
4580             else if (ch == '$' && get(pos+1) == '{')
4581                 {
4582                 //#  this is a ${substitution}
4583                 pos += 2;
4584                 String subName;
4585                 while (pos < parselen)
4586                     {
4587                     ch = get(pos);
4588                     if (ch < 0)
4589                         {
4590                         error("unterminated substitution");
4591                         return false;
4592                         }
4593                     else if (ch == '}')
4594                         break;
4595                     else
4596                         subName.push_back((char)ch);
4597                     pos++;
4598                     }
4599                 //trace("subName:%s", subName.c_str());
4600                 String subVal = attrs[subName];
4601                 //trace("subVal:%s", subVal.c_str());
4602                 attrVal.append(subVal);
4603                 }
4604             else
4605                 attrVal.push_back((char)ch);
4606             pos++;
4607             }
4609         attrVal = trim(attrVal);
4610         attrs[attrName] = attrVal;
4612         if (attrName == "Name")
4613             name = attrVal;
4614         else if (attrName == "Description")
4615             description = attrVal;
4616         else if (attrName == "Cflags")
4617             cflags = attrVal;
4618         else if (attrName == "Libs")
4619             libs = attrVal;
4620         else if (attrName == "Requires")
4621             requires = attrVal;
4622         else if (attrName == "Version")
4623             version = attrVal;
4625         //trace("name:'%s'  value:'%s'",
4626         //      attrName.c_str(), attrVal.c_str());
4627         }
4630     parseRequires();
4631     parseVersion();
4633     return true;
4636 void PkgConfig::dumpAttrs()
4638     //trace("### PkgConfig attributes for %s", fileName.c_str());
4639     std::map<String, String>::iterator iter;
4640     for (iter=attrs.begin() ; iter!=attrs.end() ; iter++)
4641         {
4642         trace("   %s = %s", iter->first.c_str(), iter->second.c_str());
4643         }
4647 bool PkgConfig::readFile(const String &fileNameArg)
4649     fileName = fileNameArg;
4651     FILE *f = fopen(fileName.c_str(), "r");
4652     if (!f)
4653         {
4654         error("cannot open file '%s' for reading", fileName.c_str());
4655         return false;
4656         }
4657     String buf;
4658     while (true)
4659         {
4660         int ch = fgetc(f);
4661         if (ch < 0)
4662             break;
4663         buf.push_back((char)ch);
4664         }
4665     fclose(f);
4667     //trace("####### File:\n%s", buf.c_str());
4668     if (!parse(buf))
4669         {
4670         return false;
4671         }
4673     dumpAttrs();
4675     return true;
4682 //########################################################################
4683 //# D E P T O O L
4684 //########################################################################
4688 /**
4689  *  Class which holds information for each file.
4690  */
4691 class FileRec
4693 public:
4695     typedef enum
4696         {
4697         UNKNOWN,
4698         CFILE,
4699         HFILE,
4700         OFILE
4701         } FileType;
4703     /**
4704      *  Constructor
4705      */
4706     FileRec()
4707         {init(); type = UNKNOWN;}
4709     /**
4710      *  Copy constructor
4711      */
4712     FileRec(const FileRec &other)
4713         {init(); assign(other);}
4714     /**
4715      *  Constructor
4716      */
4717     FileRec(int typeVal)
4718         {init(); type = typeVal;}
4719     /**
4720      *  Assignment operator
4721      */
4722     FileRec &operator=(const FileRec &other)
4723         {init(); assign(other); return *this;}
4726     /**
4727      *  Destructor
4728      */
4729     ~FileRec()
4730         {}
4732     /**
4733      *  Directory part of the file name
4734      */
4735     String path;
4737     /**
4738      *  Base name, sans directory and suffix
4739      */
4740     String baseName;
4742     /**
4743      *  File extension, such as cpp or h
4744      */
4745     String suffix;
4747     /**
4748      *  Type of file: CFILE, HFILE, OFILE
4749      */
4750     int type;
4752     /**
4753      * Used to list files ref'd by this one
4754      */
4755     std::map<String, FileRec *> files;
4758 private:
4760     void init()
4761         {
4762         }
4764     void assign(const FileRec &other)
4765         {
4766         type     = other.type;
4767         baseName = other.baseName;
4768         suffix   = other.suffix;
4769         files    = other.files;
4770         }
4772 };
4776 /**
4777  *  Simpler dependency record
4778  */
4779 class DepRec
4781 public:
4783     /**
4784      *  Constructor
4785      */
4786     DepRec()
4787         {init();}
4789     /**
4790      *  Copy constructor
4791      */
4792     DepRec(const DepRec &other)
4793         {init(); assign(other);}
4794     /**
4795      *  Constructor
4796      */
4797     DepRec(const String &fname)
4798         {init(); name = fname; }
4799     /**
4800      *  Assignment operator
4801      */
4802     DepRec &operator=(const DepRec &other)
4803         {init(); assign(other); return *this;}
4806     /**
4807      *  Destructor
4808      */
4809     ~DepRec()
4810         {}
4812     /**
4813      *  Directory part of the file name
4814      */
4815     String path;
4817     /**
4818      *  Base name, without the path and suffix
4819      */
4820     String name;
4822     /**
4823      *  Suffix of the source
4824      */
4825     String suffix;
4828     /**
4829      * Used to list files ref'd by this one
4830      */
4831     std::vector<String> files;
4834 private:
4836     void init()
4837         {
4838         }
4840     void assign(const DepRec &other)
4841         {
4842         path     = other.path;
4843         name     = other.name;
4844         suffix   = other.suffix;
4845         files    = other.files;
4846         }
4848 };
4851 class DepTool : public MakeBase
4853 public:
4855     /**
4856      *  Constructor
4857      */
4858     DepTool()
4859         {init();}
4861     /**
4862      *  Copy constructor
4863      */
4864     DepTool(const DepTool &other)
4865         {init(); assign(other);}
4867     /**
4868      *  Assignment operator
4869      */
4870     DepTool &operator=(const DepTool &other)
4871         {init(); assign(other); return *this;}
4874     /**
4875      *  Destructor
4876      */
4877     ~DepTool()
4878         {}
4881     /**
4882      *  Reset this section of code
4883      */
4884     virtual void init();
4885     
4886     /**
4887      *  Reset this section of code
4888      */
4889     virtual void assign(const DepTool &other)
4890         {
4891         }
4892     
4893     /**
4894      *  Sets the source directory which will be scanned
4895      */
4896     virtual void setSourceDirectory(const String &val)
4897         { sourceDir = val; }
4899     /**
4900      *  Returns the source directory which will be scanned
4901      */
4902     virtual String getSourceDirectory()
4903         { return sourceDir; }
4905     /**
4906      *  Sets the list of files within the directory to analyze
4907      */
4908     virtual void setFileList(const std::vector<String> &list)
4909         { fileList = list; }
4911     /**
4912      * Creates the list of all file names which will be
4913      * candidates for further processing.  Reads make.exclude
4914      * to see which files for directories to leave out.
4915      */
4916     virtual bool createFileList();
4919     /**
4920      *  Generates the forward dependency list
4921      */
4922     virtual bool generateDependencies();
4925     /**
4926      *  Generates the forward dependency list, saving the file
4927      */
4928     virtual bool generateDependencies(const String &);
4931     /**
4932      *  Load a dependency file
4933      */
4934     std::vector<DepRec> loadDepFile(const String &fileName);
4936     /**
4937      *  Load a dependency file, generating one if necessary
4938      */
4939     std::vector<DepRec> getDepFile(const String &fileName,
4940               bool forceRefresh);
4942     /**
4943      *  Save a dependency file
4944      */
4945     bool saveDepFile(const String &fileName);
4948 private:
4951     /**
4952      *
4953      */
4954     void parseName(const String &fullname,
4955                    String &path,
4956                    String &basename,
4957                    String &suffix);
4959     /**
4960      *
4961      */
4962     int get(int pos);
4964     /**
4965      *
4966      */
4967     int skipwhite(int pos);
4969     /**
4970      *
4971      */
4972     int getword(int pos, String &ret);
4974     /**
4975      *
4976      */
4977     bool sequ(int pos, char *key);
4979     /**
4980      *
4981      */
4982     bool addIncludeFile(FileRec *frec, const String &fname);
4984     /**
4985      *
4986      */
4987     bool scanFile(const String &fname, FileRec *frec);
4989     /**
4990      *
4991      */
4992     bool processDependency(FileRec *ofile,
4993                            FileRec *include,
4994                            int depth);
4996     /**
4997      *
4998      */
4999     String sourceDir;
5001     /**
5002      *
5003      */
5004     std::vector<String> fileList;
5006     /**
5007      *
5008      */
5009     std::vector<String> directories;
5011     /**
5012      * A list of all files which will be processed for
5013      * dependencies.  This is the only list that has the actual
5014      * records.  All other lists have pointers to these records.     
5015      */
5016     std::map<String, FileRec *> allFiles;
5018     /**
5019      * The list of .o files, and the
5020      * dependencies upon them.
5021      */
5022     std::map<String, FileRec *> depFiles;
5024     int depFileSize;
5025     char *depFileBuf;
5027     static const int readBufSize = 8192;
5028     char readBuf[8193];//byte larger
5030 };
5036 /**
5037  *  Clean up after processing.  Called by the destructor, but should
5038  *  also be called before the object is reused.
5039  */
5040 void DepTool::init()
5042     sourceDir = ".";
5044     fileList.clear();
5045     directories.clear();
5046     
5047     //clear refs
5048     depFiles.clear();
5049     //clear records
5050     std::map<String, FileRec *>::iterator iter;
5051     for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5052          delete iter->second;
5054     allFiles.clear(); 
5061 /**
5062  *  Parse a full path name into path, base name, and suffix
5063  */
5064 void DepTool::parseName(const String &fullname,
5065                         String &path,
5066                         String &basename,
5067                         String &suffix)
5069     if (fullname.size() < 2)
5070         return;
5072     unsigned int pos = fullname.find_last_of('/');
5073     if (pos != fullname.npos && pos<fullname.size()-1)
5074         {
5075         path = fullname.substr(0, pos);
5076         pos++;
5077         basename = fullname.substr(pos, fullname.size()-pos);
5078         }
5079     else
5080         {
5081         path = "";
5082         basename = fullname;
5083         }
5085     pos = basename.find_last_of('.');
5086     if (pos != basename.npos && pos<basename.size()-1)
5087         {
5088         suffix   = basename.substr(pos+1, basename.size()-pos-1);
5089         basename = basename.substr(0, pos);
5090         }
5092     //trace("parsename:%s %s %s", path.c_str(),
5093     //        basename.c_str(), suffix.c_str()); 
5098 /**
5099  *  Generate our internal file list.
5100  */
5101 bool DepTool::createFileList()
5104     for (unsigned int i=0 ; i<fileList.size() ; i++)
5105         {
5106         String fileName = fileList[i];
5107         //trace("## FileName:%s", fileName.c_str());
5108         String path;
5109         String basename;
5110         String sfx;
5111         parseName(fileName, path, basename, sfx);
5112         if (sfx == "cpp" || sfx == "c" || sfx == "cxx"   ||
5113             sfx == "cc" || sfx == "CC")
5114             {
5115             FileRec *fe         = new FileRec(FileRec::CFILE);
5116             fe->path            = path;
5117             fe->baseName        = basename;
5118             fe->suffix          = sfx;
5119             allFiles[fileName]  = fe;
5120             }
5121         else if (sfx == "h"   ||  sfx == "hh"  ||
5122                  sfx == "hpp" ||  sfx == "hxx")
5123             {
5124             FileRec *fe         = new FileRec(FileRec::HFILE);
5125             fe->path            = path;
5126             fe->baseName        = basename;
5127             fe->suffix          = sfx;
5128             allFiles[fileName]  = fe;
5129             }
5130         }
5132     if (!listDirectories(sourceDir, "", directories))
5133         return false;
5134         
5135     return true;
5142 /**
5143  * Get a character from the buffer at pos.  If out of range,
5144  * return -1 for safety
5145  */
5146 int DepTool::get(int pos)
5148     if (pos>depFileSize)
5149         return -1;
5150     return depFileBuf[pos];
5155 /**
5156  *  Skip over all whitespace characters beginning at pos.  Return
5157  *  the position of the first non-whitespace character.
5158  */
5159 int DepTool::skipwhite(int pos)
5161     while (pos < depFileSize)
5162         {
5163         int ch = get(pos);
5164         if (ch < 0)
5165             break;
5166         if (!isspace(ch))
5167             break;
5168         pos++;
5169         }
5170     return pos;
5174 /**
5175  *  Parse the buffer beginning at pos, for a word.  Fill
5176  *  'ret' with the result.  Return the position after the
5177  *  word.
5178  */
5179 int DepTool::getword(int pos, String &ret)
5181     while (pos < depFileSize)
5182         {
5183         int ch = get(pos);
5184         if (ch < 0)
5185             break;
5186         if (isspace(ch))
5187             break;
5188         ret.push_back((char)ch);
5189         pos++;
5190         }
5191     return pos;
5194 /**
5195  * Return whether the sequence of characters in the buffer
5196  * beginning at pos match the key,  for the length of the key
5197  */
5198 bool DepTool::sequ(int pos, char *key)
5200     while (*key)
5201         {
5202         if (*key != get(pos))
5203             return false;
5204         key++; pos++;
5205         }
5206     return true;
5211 /**
5212  *  Add an include file name to a file record.  If the name
5213  *  is not found in allFiles explicitly, try prepending include
5214  *  directory names to it and try again.
5215  */
5216 bool DepTool::addIncludeFile(FileRec *frec, const String &iname)
5219     std::map<String, FileRec *>::iterator iter =
5220            allFiles.find(iname);
5221     if (iter != allFiles.end()) //already exists
5222         {
5223          //h file in same dir
5224         FileRec *other = iter->second;
5225         //trace("local: '%s'", iname.c_str());
5226         frec->files[iname] = other;
5227         return true;
5228         }
5229     else 
5230         {
5231         //look in other dirs
5232         std::vector<String>::iterator diter;
5233         for (diter=directories.begin() ;
5234              diter!=directories.end() ; diter++)
5235             {
5236             String dfname = *diter;
5237             dfname.append("/");
5238             dfname.append(iname);
5239             iter = allFiles.find(dfname);
5240             if (iter != allFiles.end())
5241                 {
5242                 FileRec *other = iter->second;
5243                 //trace("other: '%s'", iname.c_str());
5244                 frec->files[dfname] = other;
5245                 return true;
5246                 }
5247             }
5248         }
5249     return true;
5254 /**
5255  *  Lightly parse a file to find the #include directives.  Do
5256  *  a bit of state machine stuff to make sure that the directive
5257  *  is valid.  (Like not in a comment).
5258  */
5259 bool DepTool::scanFile(const String &fname, FileRec *frec)
5261     String fileName;
5262     if (sourceDir.size() > 0)
5263         {
5264         fileName.append(sourceDir);
5265         fileName.append("/");
5266         }
5267     fileName.append(fname);
5268     String nativeName = getNativePath(fileName);
5269     FILE *f = fopen(nativeName.c_str(), "r");
5270     if (!f)
5271         {
5272         error("Could not open '%s' for reading", fname.c_str());
5273         return false;
5274         }
5275     String buf;
5276     while (!feof(f))
5277         {
5278         int len = fread(readBuf, 1, readBufSize, f);
5279         readBuf[len] = '\0';
5280         buf.append(readBuf);
5281         }
5282     fclose(f);
5284     depFileSize = buf.size();
5285     depFileBuf  = (char *)buf.c_str();
5286     int pos = 0;
5289     while (pos < depFileSize)
5290         {
5291         //trace("p:%c", get(pos));
5293         //# Block comment
5294         if (get(pos) == '/' && get(pos+1) == '*')
5295             {
5296             pos += 2;
5297             while (pos < depFileSize)
5298                 {
5299                 if (get(pos) == '*' && get(pos+1) == '/')
5300                     {
5301                     pos += 2;
5302                     break;
5303                     }
5304                 else
5305                     pos++;
5306                 }
5307             }
5308         //# Line comment
5309         else if (get(pos) == '/' && get(pos+1) == '/')
5310             {
5311             pos += 2;
5312             while (pos < depFileSize)
5313                 {
5314                 if (get(pos) == '\n')
5315                     {
5316                     pos++;
5317                     break;
5318                     }
5319                 else
5320                     pos++;
5321                 }
5322             }
5323         //# #include! yaay
5324         else if (sequ(pos, "#include"))
5325             {
5326             pos += 8;
5327             pos = skipwhite(pos);
5328             String iname;
5329             pos = getword(pos, iname);
5330             if (iname.size()>2)
5331                 {
5332                 iname = iname.substr(1, iname.size()-2);
5333                 addIncludeFile(frec, iname);
5334                 }
5335             }
5336         else
5337             {
5338             pos++;
5339             }
5340         }
5342     return true;
5347 /**
5348  *  Recursively check include lists to find all files in allFiles to which
5349  *  a given file is dependent.
5350  */
5351 bool DepTool::processDependency(FileRec *ofile,
5352                              FileRec *include,
5353                              int depth)
5355     std::map<String, FileRec *>::iterator iter;
5356     for (iter=include->files.begin() ; iter!=include->files.end() ; iter++)
5357         {
5358         String fname  = iter->first;
5359         if (ofile->files.find(fname) != ofile->files.end())
5360             {
5361             //trace("file '%s' already seen", fname.c_str());
5362             continue;
5363             }
5364         FileRec *child  = iter->second;
5365         ofile->files[fname] = child;
5366       
5367         processDependency(ofile, child, depth+1);
5368         }
5371     return true;
5378 /**
5379  *  Generate the file dependency list.
5380  */
5381 bool DepTool::generateDependencies()
5383     std::map<String, FileRec *>::iterator iter;
5384     //# First pass.  Scan for all includes
5385     for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5386         {
5387         FileRec *frec = iter->second;
5388         if (!scanFile(iter->first, frec))
5389             {
5390             //quit?
5391             }
5392         }
5394     //# Second pass.  Scan for all includes
5395     for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5396         {
5397         FileRec *include = iter->second;
5398         if (include->type == FileRec::CFILE)
5399             {
5400             String cFileName = iter->first;
5401             FileRec *ofile      = new FileRec(FileRec::OFILE);
5402             ofile->path         = include->path;
5403             ofile->baseName     = include->baseName;
5404             ofile->suffix       = include->suffix;
5405             String fname     = include->path;
5406             if (fname.size()>0)
5407                 fname.append("/");
5408             fname.append(include->baseName);
5409             fname.append(".o");
5410             depFiles[fname]    = ofile;
5411             //add the .c file first?   no, don't
5412             //ofile->files[cFileName] = include;
5413             
5414             //trace("ofile:%s", fname.c_str());
5416             processDependency(ofile, include, 0);
5417             }
5418         }
5420       
5421     return true;
5426 /**
5427  *  High-level call to generate deps and optionally save them
5428  */
5429 bool DepTool::generateDependencies(const String &fileName)
5431     if (!createFileList())
5432         return false;
5433     if (!generateDependencies())
5434         return false;
5435     if (!saveDepFile(fileName))
5436         return false;
5437     return true;
5441 /**
5442  *   This saves the dependency cache.
5443  */
5444 bool DepTool::saveDepFile(const String &fileName)
5446     time_t tim;
5447     time(&tim);
5449     FILE *f = fopen(fileName.c_str(), "w");
5450     if (!f)
5451         {
5452         trace("cannot open '%s' for writing", fileName.c_str());
5453         }
5454     fprintf(f, "<?xml version='1.0'?>\n");
5455     fprintf(f, "<!--\n");
5456     fprintf(f, "########################################################\n");
5457     fprintf(f, "## File: build.dep\n");
5458     fprintf(f, "## Generated by BuildTool at :%s", ctime(&tim));
5459     fprintf(f, "########################################################\n");
5460     fprintf(f, "-->\n");
5462     fprintf(f, "<dependencies source='%s'>\n\n", sourceDir.c_str());
5463     std::map<String, FileRec *>::iterator iter;
5464     for (iter=depFiles.begin() ; iter!=depFiles.end() ; iter++)
5465         {
5466         FileRec *frec = iter->second;
5467         if (frec->type == FileRec::OFILE)
5468             {
5469             fprintf(f, "<object path='%s' name='%s' suffix='%s'>\n",
5470                  frec->path.c_str(), frec->baseName.c_str(), frec->suffix.c_str());
5471             std::map<String, FileRec *>::iterator citer;
5472             for (citer=frec->files.begin() ; citer!=frec->files.end() ; citer++)
5473                 {
5474                 String cfname = citer->first;
5475                 fprintf(f, "    <dep name='%s'/>\n", cfname.c_str());
5476                 }
5477             fprintf(f, "</object>\n\n");
5478             }
5479         }
5481     fprintf(f, "</dependencies>\n");
5482     fprintf(f, "\n");
5483     fprintf(f, "<!--\n");
5484     fprintf(f, "########################################################\n");
5485     fprintf(f, "## E N D\n");
5486     fprintf(f, "########################################################\n");
5487     fprintf(f, "-->\n");
5489     fclose(f);
5491     return true;
5497 /**
5498  *   This loads the dependency cache.
5499  */
5500 std::vector<DepRec> DepTool::loadDepFile(const String &depFile)
5502     std::vector<DepRec> result;
5503     
5504     Parser parser;
5505     Element *root = parser.parseFile(depFile.c_str());
5506     if (!root)
5507         {
5508         //error("Could not open %s for reading", depFile.c_str());
5509         return result;
5510         }
5512     if (root->getChildren().size()==0 ||
5513         root->getChildren()[0]->getName()!="dependencies")
5514         {
5515         error("Main xml element should be <dependencies>");
5516         delete root;
5517         return result;
5518         }
5520     //########## Start parsing
5521     Element *depList = root->getChildren()[0];
5523     std::vector<Element *> objects = depList->getChildren();
5524     for (unsigned int i=0 ; i<objects.size() ; i++)
5525         {
5526         Element *objectElem = objects[i];
5527         String tagName = objectElem->getName();
5528         if (tagName == "object")
5529             {
5530             String objName   = objectElem->getAttribute("name");
5531              //trace("object:%s", objName.c_str());
5532             DepRec depObject(objName);
5533             depObject.path   = objectElem->getAttribute("path");
5534             depObject.suffix = objectElem->getAttribute("suffix");
5535             //########## DESCRIPTION
5536             std::vector<Element *> depElems = objectElem->getChildren();
5537             for (unsigned int i=0 ; i<depElems.size() ; i++)
5538                 {
5539                 Element *depElem = depElems[i];
5540                 tagName = depElem->getName();
5541                 if (tagName == "dep")
5542                     {
5543                     String depName = depElem->getAttribute("name");
5544                     //trace("    dep:%s", depName.c_str());
5545                     depObject.files.push_back(depName);
5546                     }
5547                 }
5548             //Insert into the result list, in a sorted manner
5549             bool inserted = false;
5550             std::vector<DepRec>::iterator iter;
5551             for (iter = result.begin() ; iter != result.end() ; iter++)
5552                 {
5553                 String vpath = iter->path;
5554                 vpath.append("/");
5555                 vpath.append(iter->name);
5556                 String opath = depObject.path;
5557                 opath.append("/");
5558                 opath.append(depObject.name);
5559                 if (vpath > opath)
5560                     {
5561                     inserted = true;
5562                     iter = result.insert(iter, depObject);
5563                     break;
5564                     }
5565                 }
5566             if (!inserted)
5567                 result.push_back(depObject);
5568             }
5569         }
5571     delete root;
5573     return result;
5577 /**
5578  *   This loads the dependency cache.
5579  */
5580 std::vector<DepRec> DepTool::getDepFile(const String &depFile,
5581                    bool forceRefresh)
5583     std::vector<DepRec> result;
5584     if (forceRefresh)
5585         {
5586         generateDependencies(depFile);
5587         result = loadDepFile(depFile);
5588         }
5589     else
5590         {
5591         //try once
5592         result = loadDepFile(depFile);
5593         if (result.size() == 0)
5594             {
5595             //fail? try again
5596             generateDependencies(depFile);
5597             result = loadDepFile(depFile);
5598             }
5599         }
5600     return result;
5606 //########################################################################
5607 //# T A S K
5608 //########################################################################
5609 //forward decl
5610 class Target;
5611 class Make;
5613 /**
5614  *
5615  */
5616 class Task : public MakeBase
5619 public:
5621     typedef enum
5622         {
5623         TASK_NONE,
5624         TASK_CC,
5625         TASK_COPY,
5626         TASK_DELETE,
5627         TASK_JAR,
5628         TASK_JAVAC,
5629         TASK_LINK,
5630         TASK_MAKEFILE,
5631         TASK_MKDIR,
5632         TASK_MSGFMT,
5633         TASK_RANLIB,
5634         TASK_RC,
5635         TASK_SHAREDLIB,
5636         TASK_STATICLIB,
5637         TASK_STRIP,
5638         TASK_TSTAMP
5639         } TaskType;
5640         
5642     /**
5643      *
5644      */
5645     Task(MakeBase &par) : parent(par)
5646         { init(); }
5648     /**
5649      *
5650      */
5651     Task(const Task &other) : parent(other.parent)
5652         { init(); assign(other); }
5654     /**
5655      *
5656      */
5657     Task &operator=(const Task &other)
5658         { assign(other); return *this; }
5660     /**
5661      *
5662      */
5663     virtual ~Task()
5664         { }
5667     /**
5668      *
5669      */
5670     virtual MakeBase &getParent()
5671         { return parent; }
5673      /**
5674      *
5675      */
5676     virtual int  getType()
5677         { return type; }
5679     /**
5680      *
5681      */
5682     virtual void setType(int val)
5683         { type = val; }
5685     /**
5686      *
5687      */
5688     virtual String getName()
5689         { return name; }
5691     /**
5692      *
5693      */
5694     virtual bool execute()
5695         { return true; }
5697     /**
5698      *
5699      */
5700     virtual bool parse(Element *elem)
5701         { return true; }
5703     /**
5704      *
5705      */
5706     Task *createTask(Element *elem);
5709 protected:
5711     void init()
5712         {
5713         type = TASK_NONE;
5714         name = "none";
5715         }
5717     void assign(const Task &other)
5718         {
5719         type = other.type;
5720         name = other.name;
5721         }
5722         
5723     String getAttribute(Element *elem, const String &attrName)
5724         {
5725         String str;
5726         return str;
5727         }
5729     MakeBase &parent;
5731     int type;
5733     String name;
5734 };
5738 /**
5739  * This task runs the C/C++ compiler.  The compiler is invoked
5740  * for all .c or .cpp files which are newer than their correcsponding
5741  * .o files.  
5742  */
5743 class TaskCC : public Task
5745 public:
5747     TaskCC(MakeBase &par) : Task(par)
5748         {
5749         type = TASK_CC; name = "cc";
5750         ccCommand   = "gcc";
5751         cxxCommand  = "g++";
5752         source      = ".";
5753         dest        = ".";
5754         flags       = "";
5755         defines     = "";
5756         includes    = "";
5757         fileSet.clear();
5758         }
5760     virtual ~TaskCC()
5761         {}
5763     virtual bool needsCompiling(const DepRec &depRec,
5764               const String &src, const String &dest)
5765         {
5766         return false;
5767         }
5769     virtual bool execute()
5770         {
5771         if (!listFiles(parent, fileSet))
5772             return false;
5774         bool refreshCache = false;
5775         String fullName = parent.resolve("build.dep");
5776         if (isNewerThan(parent.getURI().getPath(), fullName))
5777             {
5778             status("          : regenerating C/C++ dependency cache");
5779             refreshCache = true;
5780             }
5782         DepTool depTool;
5783         depTool.setSourceDirectory(source);
5784         depTool.setFileList(fileSet.getFiles());
5785         std::vector<DepRec> deps =
5786              depTool.getDepFile("build.dep", refreshCache);
5787         
5788         String incs;
5789         incs.append("-I");
5790         incs.append(parent.resolve("."));
5791         incs.append(" ");
5792         if (includes.size()>0)
5793             {
5794             incs.append(includes);
5795             incs.append(" ");
5796             }
5797         std::set<String> paths;
5798         std::vector<DepRec>::iterator viter;
5799         for (viter=deps.begin() ; viter!=deps.end() ; viter++)
5800             {
5801             DepRec dep = *viter;
5802             if (dep.path.size()>0)
5803                 paths.insert(dep.path);
5804             }
5805         if (source.size()>0)
5806             {
5807             incs.append(" -I");
5808             incs.append(parent.resolve(source));
5809             incs.append(" ");
5810             }
5811         std::set<String>::iterator setIter;
5812         for (setIter=paths.begin() ; setIter!=paths.end() ; setIter++)
5813             {
5814             incs.append(" -I");
5815             String dname;
5816             if (source.size()>0)
5817                 {
5818                 dname.append(source);
5819                 dname.append("/");
5820                 }
5821             dname.append(*setIter);
5822             incs.append(parent.resolve(dname));
5823             }
5824         std::vector<String> cfiles;
5825         for (viter=deps.begin() ; viter!=deps.end() ; viter++)
5826             {
5827             DepRec dep = *viter;
5829             //## Select command
5830             String sfx = dep.suffix;
5831             String command = ccCommand;
5832             if (sfx == "cpp" || sfx == "c++" || sfx == "cc"
5833                  || sfx == "CC")
5834                 command = cxxCommand;
5835  
5836             //## Make paths
5837             String destPath = dest;
5838             String srcPath  = source;
5839             if (dep.path.size()>0)
5840                 {
5841                 destPath.append("/");
5842                 destPath.append(dep.path);
5843                 srcPath.append("/");
5844                 srcPath.append(dep.path);
5845                 }
5846             //## Make sure destination directory exists
5847             if (!createDirectory(destPath))
5848                 return false;
5849                 
5850             //## Check whether it needs to be done
5851             String destName;
5852             if (destPath.size()>0)
5853                 {
5854                 destName.append(destPath);
5855                 destName.append("/");
5856                 }
5857             destName.append(dep.name);
5858             destName.append(".o");
5859             String destFullName = parent.resolve(destName);
5860             String srcName;
5861             if (srcPath.size()>0)
5862                 {
5863                 srcName.append(srcPath);
5864                 srcName.append("/");
5865                 }
5866             srcName.append(dep.name);
5867             srcName.append(".");
5868             srcName.append(dep.suffix);
5869             String srcFullName = parent.resolve(srcName);
5870             bool compileMe = false;
5871             if (isNewerThan(srcFullName, destFullName))
5872                 {
5873                 status("          : compile of %s required by %s",
5874                         destFullName.c_str(), srcFullName.c_str());
5875                 compileMe = true;
5876                 }
5877             else
5878                 {
5879                 for (unsigned int i=0 ; i<dep.files.size() ; i++)
5880                     {
5881                     String depName;
5882                     if (srcPath.size()>0)
5883                         {
5884                         depName.append(srcPath);
5885                         depName.append("/");
5886                         }
5887                     depName.append(dep.files[i]);
5888                     String depFullName = parent.resolve(depName);
5889                     if (isNewerThan(depFullName, destFullName))
5890                         {
5891                         status("          : compile of %s required by %s",
5892                                 destFullName.c_str(), depFullName.c_str());
5893                         compileMe = true;
5894                         break;
5895                         }
5896                     }
5897                 }
5898             if (!compileMe)
5899                 {
5900                 continue;
5901                 }
5903             //## Assemble the command
5904             String cmd = command;
5905             cmd.append(" -c ");
5906             cmd.append(flags);
5907             cmd.append(" ");
5908             cmd.append(defines);
5909             cmd.append(" ");
5910             cmd.append(incs);
5911             cmd.append(" ");
5912             cmd.append(srcFullName);
5913             cmd.append(" -o ");
5914             cmd.append(destFullName);
5916             //## Execute the command
5918             String outString, errString;
5919             if (!executeCommand(cmd.c_str(), "", outString, errString))
5920                 {
5921                 error("problem compiling: %s", errString.c_str());
5922                 return false;
5923                 }
5924             }
5925         
5926         return true;
5927         }
5929     virtual bool parse(Element *elem)
5930         {
5931         String s;
5932         if (!parent.getAttribute(elem, "command", s))
5933             return false;
5934         if (s.size()>0) { ccCommand = s; cxxCommand = s; }
5935         if (!parent.getAttribute(elem, "cc", s))
5936             return false;
5937         if (s.size()>0) ccCommand = s;
5938         if (!parent.getAttribute(elem, "cxx", s))
5939             return false;
5940         if (s.size()>0) cxxCommand = s;
5941         if (!parent.getAttribute(elem, "destdir", s))
5942             return false;
5943         if (s.size()>0) dest = s;
5945         std::vector<Element *> children = elem->getChildren();
5946         for (unsigned int i=0 ; i<children.size() ; i++)
5947             {
5948             Element *child = children[i];
5949             String tagName = child->getName();
5950             if (tagName == "flags")
5951                 {
5952                 if (!parent.getValue(child, flags))
5953                     return false;
5954                 flags = strip(flags);
5955                 }
5956             else if (tagName == "includes")
5957                 {
5958                 if (!parent.getValue(child, includes))
5959                     return false;
5960                 includes = strip(includes);
5961                 }
5962             else if (tagName == "defines")
5963                 {
5964                 if (!parent.getValue(child, defines))
5965                     return false;
5966                 defines = strip(defines);
5967                 }
5968             else if (tagName == "fileset")
5969                 {
5970                 if (!parseFileSet(child, parent, fileSet))
5971                     return false;
5972                 source = fileSet.getDirectory();
5973                 }
5974             }
5976         return true;
5977         }
5978         
5979 protected:
5981     String ccCommand;
5982     String cxxCommand;
5983     String source;
5984     String dest;
5985     String flags;
5986     String defines;
5987     String includes;
5988     FileSet fileSet;
5989     
5990 };
5994 /**
5995  *
5996  */
5997 class TaskCopy : public Task
5999 public:
6001     typedef enum
6002         {
6003         CP_NONE,
6004         CP_TOFILE,
6005         CP_TODIR
6006         } CopyType;
6008     TaskCopy(MakeBase &par) : Task(par)
6009         {
6010         type = TASK_COPY; name = "copy";
6011         cptype = CP_NONE;
6012         verbose = false;
6013         haveFileSet = false;
6014         }
6016     virtual ~TaskCopy()
6017         {}
6019     virtual bool execute()
6020         {
6021         switch (cptype)
6022            {
6023            case CP_TOFILE:
6024                {
6025                if (fileName.size()>0)
6026                    {
6027                    status("          : %s to %s",
6028                         fileName.c_str(), toFileName.c_str());
6029                    String fullSource = parent.resolve(fileName);
6030                    String fullDest = parent.resolve(toFileName);
6031                    //trace("copy %s to file %s", fullSource.c_str(),
6032                    //                       fullDest.c_str());
6033                    if (!isRegularFile(fullSource))
6034                        {
6035                        error("copy : file %s does not exist", fullSource.c_str());
6036                        return false;
6037                        }
6038                    if (!isNewerThan(fullSource, fullDest))
6039                        {
6040                        return true;
6041                        }
6042                    if (!copyFile(fullSource, fullDest))
6043                        return false;
6044                    status("          : 1 file copied");
6045                    }
6046                return true;
6047                }
6048            case CP_TODIR:
6049                {
6050                if (haveFileSet)
6051                    {
6052                    if (!listFiles(parent, fileSet))
6053                        return false;
6054                    String fileSetDir = fileSet.getDirectory();
6056                    status("          : %s to %s",
6057                        fileSetDir.c_str(), toDirName.c_str());
6059                    int nrFiles = 0;
6060                    for (unsigned int i=0 ; i<fileSet.size() ; i++)
6061                        {
6062                        String fileName = fileSet[i];
6064                        String sourcePath;
6065                        if (fileSetDir.size()>0)
6066                            {
6067                            sourcePath.append(fileSetDir);
6068                            sourcePath.append("/");
6069                            }
6070                        sourcePath.append(fileName);
6071                        String fullSource = parent.resolve(sourcePath);
6072                        
6073                        //Get the immediate parent directory's base name
6074                        String baseFileSetDir = fileSetDir;
6075                        unsigned int pos = baseFileSetDir.find_last_of('/');
6076                        if (pos!=baseFileSetDir.npos &&
6077                                   pos < baseFileSetDir.size()-1)
6078                            baseFileSetDir =
6079                               baseFileSetDir.substr(pos+1,
6080                                    baseFileSetDir.size());
6081                        //Now make the new path
6082                        String destPath;
6083                        if (toDirName.size()>0)
6084                            {
6085                            destPath.append(toDirName);
6086                            destPath.append("/");
6087                            }
6088                        if (baseFileSetDir.size()>0)
6089                            {
6090                            destPath.append(baseFileSetDir);
6091                            destPath.append("/");
6092                            }
6093                        destPath.append(fileName);
6094                        String fullDest = parent.resolve(destPath);
6095                        //trace("fileName:%s", fileName.c_str());
6096                        //trace("copy %s to new dir : %s", fullSource.c_str(),
6097                        //                   fullDest.c_str());
6098                        if (!isNewerThan(fullSource, fullDest))
6099                            {
6100                            //trace("copy skipping %s", fullSource.c_str());
6101                            continue;
6102                            }
6103                        if (!copyFile(fullSource, fullDest))
6104                            return false;
6105                        nrFiles++;
6106                        }
6107                    status("          : %d file(s) copied", nrFiles);
6108                    }
6109                else //file source
6110                    {
6111                    //For file->dir we want only the basename of
6112                    //the source appended to the dest dir
6113                    status("          : %s to %s", 
6114                        fileName.c_str(), toDirName.c_str());
6115                    String baseName = fileName;
6116                    unsigned int pos = baseName.find_last_of('/');
6117                    if (pos!=baseName.npos && pos<baseName.size()-1)
6118                        baseName = baseName.substr(pos+1, baseName.size());
6119                    String fullSource = parent.resolve(fileName);
6120                    String destPath;
6121                    if (toDirName.size()>0)
6122                        {
6123                        destPath.append(toDirName);
6124                        destPath.append("/");
6125                        }
6126                    destPath.append(baseName);
6127                    String fullDest = parent.resolve(destPath);
6128                    //trace("copy %s to new dir : %s", fullSource.c_str(),
6129                    //                       fullDest.c_str());
6130                    if (!isRegularFile(fullSource))
6131                        {
6132                        error("copy : file %s does not exist", fullSource.c_str());
6133                        return false;
6134                        }
6135                    if (!isNewerThan(fullSource, fullDest))
6136                        {
6137                        return true;
6138                        }
6139                    if (!copyFile(fullSource, fullDest))
6140                        return false;
6141                    status("          : 1 file copied");
6142                    }
6143                return true;
6144                }
6145            }
6146         return true;
6147         }
6150     virtual bool parse(Element *elem)
6151         {
6152         if (!parent.getAttribute(elem, "file", fileName))
6153             return false;
6154         if (!parent.getAttribute(elem, "tofile", toFileName))
6155             return false;
6156         if (toFileName.size() > 0)
6157             cptype = CP_TOFILE;
6158         if (!parent.getAttribute(elem, "todir", toDirName))
6159             return false;
6160         if (toDirName.size() > 0)
6161             cptype = CP_TODIR;
6162         String ret;
6163         if (!parent.getAttribute(elem, "verbose", ret))
6164             return false;
6165         if (ret.size()>0 && !getBool(ret, verbose))
6166             return false;
6167             
6168         haveFileSet = false;
6169         
6170         std::vector<Element *> children = elem->getChildren();
6171         for (unsigned int i=0 ; i<children.size() ; i++)
6172             {
6173             Element *child = children[i];
6174             String tagName = child->getName();
6175             if (tagName == "fileset")
6176                 {
6177                 if (!parseFileSet(child, parent, fileSet))
6178                     {
6179                     error("problem getting fileset");
6180                     return false;
6181                     }
6182                 haveFileSet = true;
6183                 }
6184             }
6186         //Perform validity checks
6187         if (fileName.size()>0 && fileSet.size()>0)
6188             {
6189             error("<copy> can only have one of : file= and <fileset>");
6190             return false;
6191             }
6192         if (toFileName.size()>0 && toDirName.size()>0)
6193             {
6194             error("<copy> can only have one of : tofile= or todir=");
6195             return false;
6196             }
6197         if (haveFileSet && toDirName.size()==0)
6198             {
6199             error("a <copy> task with a <fileset> must have : todir=");
6200             return false;
6201             }
6202         if (cptype == CP_TOFILE && fileName.size()==0)
6203             {
6204             error("<copy> tofile= must be associated with : file=");
6205             return false;
6206             }
6207         if (cptype == CP_TODIR && fileName.size()==0 && !haveFileSet)
6208             {
6209             error("<copy> todir= must be associated with : file= or <fileset>");
6210             return false;
6211             }
6213         return true;
6214         }
6215         
6216 private:
6218     int cptype;
6219     String fileName;
6220     FileSet fileSet;
6221     String toFileName;
6222     String toDirName;
6223     bool verbose;
6224     bool haveFileSet;
6225 };
6228 /**
6229  *
6230  */
6231 class TaskDelete : public Task
6233 public:
6235     typedef enum
6236         {
6237         DEL_FILE,
6238         DEL_DIR,
6239         DEL_FILESET
6240         } DeleteType;
6242     TaskDelete(MakeBase &par) : Task(par)
6243         { 
6244           type        = TASK_DELETE;
6245           name        = "delete";
6246           delType     = DEL_FILE;
6247           verbose     = false;
6248           quiet       = false;
6249           failOnError = true;
6250         }
6252     virtual ~TaskDelete()
6253         {}
6255     virtual bool execute()
6256         {
6257         struct stat finfo;
6258         switch (delType)
6259             {
6260             case DEL_FILE:
6261                 {
6262                 status("          : %s", fileName.c_str());
6263                 String fullName = parent.resolve(fileName);
6264                 char *fname = (char *)fullName.c_str();
6265                 //does not exist
6266                 if (stat(fname, &finfo)<0)
6267                     return true;
6268                 //exists but is not a regular file
6269                 if (!S_ISREG(finfo.st_mode))
6270                     {
6271                     error("<delete> failed. '%s' exists and is not a regular file",
6272                           fname);
6273                     return false;
6274                     }
6275                 if (remove(fname)<0)
6276                     {
6277                     error("<delete> failed: %s", strerror(errno));
6278                     return false;
6279                     }
6280                 return true;
6281                 }
6282             case DEL_DIR:
6283                 {
6284                 status("          : %s", dirName.c_str());
6285                 String fullDir = parent.resolve(dirName);
6286                 if (!removeDirectory(fullDir))
6287                     return false;
6288                 return true;
6289                 }
6290             }
6291         return true;
6292         }
6294     virtual bool parse(Element *elem)
6295         {
6296         if (!parent.getAttribute(elem, "file", fileName))
6297             return false;
6298         if (fileName.size() > 0)
6299             delType = DEL_FILE;
6300         if (!parent.getAttribute(elem, "dir", dirName))
6301             return false;
6302         if (dirName.size() > 0)
6303             delType = DEL_DIR;
6304         if (fileName.size()>0 && dirName.size()>0)
6305             {
6306             error("<delete> can only have one attribute of file= or dir=");
6307             return false;
6308             }
6309         String ret;
6310         if (!parent.getAttribute(elem, "verbose", ret))
6311             return false;
6312         if (ret.size()>0 && !getBool(ret, verbose))
6313             return false;
6314         if (!parent.getAttribute(elem, "quiet", ret))
6315             return false;
6316         if (ret.size()>0 && !getBool(ret, quiet))
6317             return false;
6318         if (!parent.getAttribute(elem, "failonerror", ret))
6319             return false;
6320         if (ret.size()>0 && !getBool(ret, failOnError))
6321             return false;
6322         return true;
6323         }
6325 private:
6327     int delType;
6328     String dirName;
6329     String fileName;
6330     bool verbose;
6331     bool quiet;
6332     bool failOnError;
6333 };
6336 /**
6337  *
6338  */
6339 class TaskJar : public Task
6341 public:
6343     TaskJar(MakeBase &par) : Task(par)
6344         { type = TASK_JAR; name = "jar"; }
6346     virtual ~TaskJar()
6347         {}
6349     virtual bool execute()
6350         {
6351         return true;
6352         }
6354     virtual bool parse(Element *elem)
6355         {
6356         return true;
6357         }
6358 };
6361 /**
6362  *
6363  */
6364 class TaskJavac : public Task
6366 public:
6368     TaskJavac(MakeBase &par) : Task(par)
6369         { type = TASK_JAVAC; name = "javac"; }
6371     virtual ~TaskJavac()
6372         {}
6374     virtual bool execute()
6375         {
6376         return true;
6377         }
6379     virtual bool parse(Element *elem)
6380         {
6381         return true;
6382         }
6383 };
6386 /**
6387  *
6388  */
6389 class TaskLink : public Task
6391 public:
6393     TaskLink(MakeBase &par) : Task(par)
6394         {
6395         type = TASK_LINK; name = "link";
6396         command = "g++";
6397         doStrip = false;
6398                 stripCommand = "strip";
6399                 objcopyCommand = "objcopy";
6400         }
6402     virtual ~TaskLink()
6403         {}
6405     virtual bool execute()
6406         {
6407         if (!listFiles(parent, fileSet))
6408             return false;
6409         String fileSetDir = fileSet.getDirectory();
6410         //trace("%d files in %s", fileSet.size(), fileSetDir.c_str());
6411         bool doit = false;
6412         String fullTarget = parent.resolve(fileName);
6413         String cmd = command;
6414         cmd.append(" -o ");
6415         cmd.append(fullTarget);
6416         cmd.append(" ");
6417         cmd.append(flags);
6418         for (unsigned int i=0 ; i<fileSet.size() ; i++)
6419             {
6420             cmd.append(" ");
6421             String obj;
6422             if (fileSetDir.size()>0)
6423                 {
6424                 obj.append(fileSetDir);
6425                 obj.append("/");
6426                 }
6427             obj.append(fileSet[i]);
6428             String fullObj = parent.resolve(obj);
6429             cmd.append(fullObj);
6430             //trace("link: tgt:%s obj:%s", fullTarget.c_str(),
6431             //          fullObj.c_str());
6432             if (isNewerThan(fullObj, fullTarget))
6433                 doit = true;
6434             }
6435         cmd.append(" ");
6436         cmd.append(libs);
6437         if (!doit)
6438             {
6439             //trace("link not needed");
6440             return true;
6441             }
6442         //trace("LINK cmd:%s", cmd.c_str());
6445         String outbuf, errbuf;
6446         if (!executeCommand(cmd.c_str(), "", outbuf, errbuf))
6447             {
6448             error("LINK problem: %s", errbuf.c_str());
6449             return false;
6450             }
6452         if (symFileName.size()>0)
6453             {
6454             String symFullName = parent.resolve(symFileName);
6455             cmd = objcopyCommand;
6456             cmd.append(" --only-keep-debug ");
6457             cmd.append(getNativePath(fullTarget));
6458             cmd.append(" ");
6459             cmd.append(getNativePath(symFullName));
6460             if (!executeCommand(cmd, "", outbuf, errbuf))
6461                 {
6462                 error("<strip> symbol file failed : %s", errbuf.c_str());
6463                 return false;
6464                 }
6465             }
6466             
6467         if (doStrip)
6468             {
6469             cmd = stripCommand;
6470             cmd.append(" ");
6471             cmd.append(getNativePath(fullTarget));
6472             if (!executeCommand(cmd, "", outbuf, errbuf))
6473                {
6474                error("<strip> failed : %s", errbuf.c_str());
6475                return false;
6476                }
6477             }
6479         return true;
6480         }
6482     virtual bool parse(Element *elem)
6483         {
6484         String s;
6485         if (!parent.getAttribute(elem, "command", s))
6486             return false;
6487         if (s.size()>0)
6488             command = s;
6489         if (!parent.getAttribute(elem, "objcopycommand", s))
6490             return false;
6491         if (s.size()>0)
6492             objcopyCommand = s;
6493         if (!parent.getAttribute(elem, "stripcommand", s))
6494             return false;
6495         if (s.size()>0)
6496             stripCommand = s;
6497         if (!parent.getAttribute(elem, "out", fileName))
6498             return false;
6499         if (!parent.getAttribute(elem, "strip", s))
6500             return false;
6501         if (!getBool(s, doStrip))
6502             return false;
6503         if (!parent.getAttribute(elem, "symfile", symFileName))
6504             return false;
6505             
6506         std::vector<Element *> children = elem->getChildren();
6507         for (unsigned int i=0 ; i<children.size() ; i++)
6508             {
6509             Element *child = children[i];
6510             String tagName = child->getName();
6511             if (tagName == "fileset")
6512                 {
6513                 if (!parseFileSet(child, parent, fileSet))
6514                     return false;
6515                 }
6516             else if (tagName == "flags")
6517                 {
6518                 if (!parent.getValue(child, flags))
6519                     return false;
6520                 flags = strip(flags);
6521                 }
6522             else if (tagName == "libs")
6523                 {
6524                 if (!parent.getValue(child, libs))
6525                     return false;
6526                 libs = strip(libs);
6527                 }
6528             }
6529         return true;
6530         }
6532 private:
6534     String  command;
6535     String  fileName;
6536     String  flags;
6537     String  libs;
6538     FileSet fileSet;
6539     bool    doStrip;
6540     String  symFileName;
6541     String  stripCommand;
6542     String  objcopyCommand;
6544 };
6548 /**
6549  * Create a named directory
6550  */
6551 class TaskMakeFile : public Task
6553 public:
6555     TaskMakeFile(MakeBase &par) : Task(par)
6556         { type = TASK_MAKEFILE; name = "makefile"; }
6558     virtual ~TaskMakeFile()
6559         {}
6561     virtual bool execute()
6562         {
6563         status("          : %s", fileName.c_str());
6564         String fullName = parent.resolve(fileName);
6565         if (!isNewerThan(parent.getURI().getPath(), fullName))
6566             {
6567             //trace("skipped <makefile>");
6568             return true;
6569             }
6570         //trace("fullName:%s", fullName.c_str());
6571         FILE *f = fopen(fullName.c_str(), "w");
6572         if (!f)
6573             {
6574             error("<makefile> could not open %s for writing : %s",
6575                 fullName.c_str(), strerror(errno));
6576             return false;
6577             }
6578         for (unsigned int i=0 ; i<text.size() ; i++)
6579             fputc(text[i], f);
6580         fputc('\n', f);
6581         fclose(f);
6582         return true;
6583         }
6585     virtual bool parse(Element *elem)
6586         {
6587         if (!parent.getAttribute(elem, "file", fileName))
6588             return false;
6589         if (fileName.size() == 0)
6590             {
6591             error("<makefile> requires 'file=\"filename\"' attribute");
6592             return false;
6593             }
6594         if (!parent.getValue(elem, text))
6595             return false;
6596         text = leftJustify(text);
6597         //trace("dirname:%s", dirName.c_str());
6598         return true;
6599         }
6601 private:
6603     String fileName;
6604     String text;
6605 };
6609 /**
6610  * Create a named directory
6611  */
6612 class TaskMkDir : public Task
6614 public:
6616     TaskMkDir(MakeBase &par) : Task(par)
6617         { type = TASK_MKDIR; name = "mkdir"; }
6619     virtual ~TaskMkDir()
6620         {}
6622     virtual bool execute()
6623         {
6624         status("          : %s", dirName.c_str());
6625         String fullDir = parent.resolve(dirName);
6626         //trace("fullDir:%s", fullDir.c_str());
6627         if (!createDirectory(fullDir))
6628             return false;
6629         return true;
6630         }
6632     virtual bool parse(Element *elem)
6633         {
6634         if (!parent.getAttribute(elem, "dir", dirName))
6635             return false;
6636         if (dirName.size() == 0)
6637             {
6638             error("<mkdir> requires 'dir=\"dirname\"' attribute");
6639             return false;
6640             }
6641         return true;
6642         }
6644 private:
6646     String dirName;
6647 };
6651 /**
6652  * Create a named directory
6653  */
6654 class TaskMsgFmt: public Task
6656 public:
6658     TaskMsgFmt(MakeBase &par) : Task(par)
6659          {
6660          type    = TASK_MSGFMT;
6661          name    = "msgfmt";
6662          command = "msgfmt";
6663          owndir  = false;
6664          }
6666     virtual ~TaskMsgFmt()
6667         {}
6669     virtual bool execute()
6670         {
6671         if (!listFiles(parent, fileSet))
6672             return false;
6673         String fileSetDir = fileSet.getDirectory();
6675         //trace("msgfmt: %d", fileSet.size());
6676         for (unsigned int i=0 ; i<fileSet.size() ; i++)
6677             {
6678             String fileName = fileSet[i];
6679             if (getSuffix(fileName) != "po")
6680                 continue;
6681             String sourcePath;
6682             if (fileSetDir.size()>0)
6683                 {
6684                 sourcePath.append(fileSetDir);
6685                 sourcePath.append("/");
6686                 }
6687             sourcePath.append(fileName);
6688             String fullSource = parent.resolve(sourcePath);
6690             String destPath;
6691             if (toDirName.size()>0)
6692                 {
6693                 destPath.append(toDirName);
6694                 destPath.append("/");
6695                 }
6696             if (owndir)
6697                 {
6698                 String subdir = fileName;
6699                 unsigned int pos = subdir.find_last_of('.');
6700                 if (pos != subdir.npos)
6701                     subdir = subdir.substr(0, pos);
6702                 destPath.append(subdir);
6703                 destPath.append("/");
6704                 }
6705             destPath.append(fileName);
6706             destPath[destPath.size()-2] = 'm';
6707             String fullDest = parent.resolve(destPath);
6709             if (!isNewerThan(fullSource, fullDest))
6710                 {
6711                 //trace("skip %s", fullSource.c_str());
6712                 continue;
6713                 }
6714                 
6715             String cmd = command;
6716             cmd.append(" ");
6717             cmd.append(fullSource);
6718             cmd.append(" -o ");
6719             cmd.append(fullDest);
6720             
6721             int pos = fullDest.find_last_of('/');
6722             if (pos>0)
6723                 {
6724                 String fullDestPath = fullDest.substr(0, pos);
6725                 if (!createDirectory(fullDestPath))
6726                     return false;
6727                 }
6731             String outString, errString;
6732             if (!executeCommand(cmd.c_str(), "", outString, errString))
6733                 {
6734                 error("<msgfmt> problem: %s", errString.c_str());
6735                 return false;
6736                 }
6737             }
6739         return true;
6740         }
6742     virtual bool parse(Element *elem)
6743         {
6744         String s;
6745         if (!parent.getAttribute(elem, "command", s))
6746             return false;
6747         if (s.size()>0)
6748             command = s;
6749         if (!parent.getAttribute(elem, "todir", toDirName))
6750             return false;
6751         if (!parent.getAttribute(elem, "owndir", s))
6752             return false;
6753         if (!getBool(s, owndir))
6754             return false;
6755             
6756         std::vector<Element *> children = elem->getChildren();
6757         for (unsigned int i=0 ; i<children.size() ; i++)
6758             {
6759             Element *child = children[i];
6760             String tagName = child->getName();
6761             if (tagName == "fileset")
6762                 {
6763                 if (!parseFileSet(child, parent, fileSet))
6764                     return false;
6765                 }
6766             }
6767         return true;
6768         }
6770 private:
6772     String command;
6773     String toDirName;
6774     FileSet fileSet;
6775     bool owndir;
6777 };
6783 /**
6784  *  Process an archive to allow random access
6785  */
6786 class TaskRanlib : public Task
6788 public:
6790     TaskRanlib(MakeBase &par) : Task(par)
6791         {
6792         type = TASK_RANLIB; name = "ranlib";
6793         command = "ranlib";
6794         }
6796     virtual ~TaskRanlib()
6797         {}
6799     virtual bool execute()
6800         {
6801         String fullName = parent.resolve(fileName);
6802         //trace("fullDir:%s", fullDir.c_str());
6803         String cmd = command;
6804         cmd.append(" ");
6805         cmd.append(fullName);
6806         String outbuf, errbuf;
6807         if (!executeCommand(cmd, "", outbuf, errbuf))
6808             return false;
6809         return true;
6810         }
6812     virtual bool parse(Element *elem)
6813         {
6814         String s;
6815         if (!parent.getAttribute(elem, "command", s))
6816             return false;
6817         if (s.size()>0)
6818            command = s;
6819         if (!parent.getAttribute(elem, "file", fileName))
6820             return false;
6821         if (fileName.size() == 0)
6822             {
6823             error("<ranlib> requires 'file=\"fileNname\"' attribute");
6824             return false;
6825             }
6826         return true;
6827         }
6829 private:
6831     String fileName;
6832     String command;
6833 };
6837 /**
6838  * Run the "ar" command to archive .o's into a .a
6839  */
6840 class TaskRC : public Task
6842 public:
6844     TaskRC(MakeBase &par) : Task(par)
6845         {
6846         type = TASK_RC; name = "rc";
6847         command = "windres";
6848         }
6850     virtual ~TaskRC()
6851         {}
6853     virtual bool execute()
6854         {
6855         String fullFile = parent.resolve(fileName);
6856         String fullOut  = parent.resolve(outName);
6857         if (!isNewerThan(fullFile, fullOut))
6858             return true;
6859         String cmd = command;
6860         cmd.append(" -o ");
6861         cmd.append(fullOut);
6862         cmd.append(" ");
6863         cmd.append(flags);
6864         cmd.append(" ");
6865         cmd.append(fullFile);
6867         String outString, errString;
6868         if (!executeCommand(cmd.c_str(), "", outString, errString))
6869             {
6870             error("RC problem: %s", errString.c_str());
6871             return false;
6872             }
6873         return true;
6874         }
6876     virtual bool parse(Element *elem)
6877         {
6878         if (!parent.getAttribute(elem, "command", command))
6879             return false;
6880         if (!parent.getAttribute(elem, "file", fileName))
6881             return false;
6882         if (!parent.getAttribute(elem, "out", outName))
6883             return false;
6884         std::vector<Element *> children = elem->getChildren();
6885         for (unsigned int i=0 ; i<children.size() ; i++)
6886             {
6887             Element *child = children[i];
6888             String tagName = child->getName();
6889             if (tagName == "flags")
6890                 {
6891                 if (!parent.getValue(child, flags))
6892                     return false;
6893                 }
6894             }
6895         return true;
6896         }
6898 private:
6900     String command;
6901     String flags;
6902     String fileName;
6903     String outName;
6905 };
6909 /**
6910  *  Collect .o's into a .so or DLL
6911  */
6912 class TaskSharedLib : public Task
6914 public:
6916     TaskSharedLib(MakeBase &par) : Task(par)
6917         {
6918         type = TASK_SHAREDLIB; name = "dll";
6919         command = "ar crv";
6920         }
6922     virtual ~TaskSharedLib()
6923         {}
6925     virtual bool execute()
6926         {
6927         //trace("###########HERE %d", fileSet.size());
6928         bool doit = false;
6929         
6930         String fullOut = parent.resolve(fileName);
6931         //trace("ar fullout: %s", fullOut.c_str());
6932         
6933         if (!listFiles(parent, fileSet))
6934             return false;
6935         String fileSetDir = fileSet.getDirectory();
6937         for (unsigned int i=0 ; i<fileSet.size() ; i++)
6938             {
6939             String fname;
6940             if (fileSetDir.size()>0)
6941                 {
6942                 fname.append(fileSetDir);
6943                 fname.append("/");
6944                 }
6945             fname.append(fileSet[i]);
6946             String fullName = parent.resolve(fname);
6947             //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
6948             if (isNewerThan(fullName, fullOut))
6949                 doit = true;
6950             }
6951         //trace("Needs it:%d", doit);
6952         if (!doit)
6953             {
6954             return true;
6955             }
6957         String cmd = "dllwrap";
6958         cmd.append(" -o ");
6959         cmd.append(fullOut);
6960         if (defFileName.size()>0)
6961             {
6962             cmd.append(" --def ");
6963             cmd.append(defFileName);
6964             cmd.append(" ");
6965             }
6966         if (impFileName.size()>0)
6967             {
6968             cmd.append(" --implib ");
6969             cmd.append(impFileName);
6970             cmd.append(" ");
6971             }
6972         for (unsigned int i=0 ; i<fileSet.size() ; i++)
6973             {
6974             String fname;
6975             if (fileSetDir.size()>0)
6976                 {
6977                 fname.append(fileSetDir);
6978                 fname.append("/");
6979                 }
6980             fname.append(fileSet[i]);
6981             String fullName = parent.resolve(fname);
6983             cmd.append(" ");
6984             cmd.append(fullName);
6985             }
6986         cmd.append(" ");
6987         cmd.append(libs);
6989         String outString, errString;
6990         if (!executeCommand(cmd.c_str(), "", outString, errString))
6991             {
6992             error("<sharedlib> problem: %s", errString.c_str());
6993             return false;
6994             }
6996         return true;
6997         }
6999     virtual bool parse(Element *elem)
7000         {
7001         if (!parent.getAttribute(elem, "file", fileName))
7002             return false;
7003         if (!parent.getAttribute(elem, "import", impFileName))
7004             return false;
7005         if (!parent.getAttribute(elem, "def", defFileName))
7006             return false;
7007             
7008         std::vector<Element *> children = elem->getChildren();
7009         for (unsigned int i=0 ; i<children.size() ; i++)
7010             {
7011             Element *child = children[i];
7012             String tagName = child->getName();
7013             if (tagName == "fileset")
7014                 {
7015                 if (!parseFileSet(child, parent, fileSet))
7016                     return false;
7017                 }
7018             else if (tagName == "libs")
7019                 {
7020                 if (!parent.getValue(child, libs))
7021                     return false;
7022                 libs = strip(libs);
7023                 }
7024             }
7025         return true;
7026         }
7028 private:
7030     String command;
7031     String fileName;
7032     String defFileName;
7033     String impFileName;
7034     FileSet fileSet;
7035     String libs;
7037 };
7040 /**
7041  * Run the "ar" command to archive .o's into a .a
7042  */
7043 class TaskStaticLib : public Task
7045 public:
7047     TaskStaticLib(MakeBase &par) : Task(par)
7048         {
7049         type = TASK_STATICLIB; name = "staticlib";
7050         command = "ar crv";
7051         }
7053     virtual ~TaskStaticLib()
7054         {}
7056     virtual bool execute()
7057         {
7058         //trace("###########HERE %d", fileSet.size());
7059         bool doit = false;
7060         
7061         String fullOut = parent.resolve(fileName);
7062         //trace("ar fullout: %s", fullOut.c_str());
7063         
7064         if (!listFiles(parent, fileSet))
7065             return false;
7066         String fileSetDir = fileSet.getDirectory();
7068         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7069             {
7070             String fname;
7071             if (fileSetDir.size()>0)
7072                 {
7073                 fname.append(fileSetDir);
7074                 fname.append("/");
7075                 }
7076             fname.append(fileSet[i]);
7077             String fullName = parent.resolve(fname);
7078             //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
7079             if (isNewerThan(fullName, fullOut))
7080                 doit = true;
7081             }
7082         //trace("Needs it:%d", doit);
7083         if (!doit)
7084             {
7085             return true;
7086             }
7088         String cmd = command;
7089         cmd.append(" ");
7090         cmd.append(fullOut);
7091         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7092             {
7093             String fname;
7094             if (fileSetDir.size()>0)
7095                 {
7096                 fname.append(fileSetDir);
7097                 fname.append("/");
7098                 }
7099             fname.append(fileSet[i]);
7100             String fullName = parent.resolve(fname);
7102             cmd.append(" ");
7103             cmd.append(fullName);
7104             }
7106         String outString, errString;
7107         if (!executeCommand(cmd.c_str(), "", outString, errString))
7108             {
7109             error("<staticlib> problem: %s", errString.c_str());
7110             return false;
7111             }
7113         return true;
7114         }
7116     virtual bool parse(Element *elem)
7117         {
7118         String s;
7119         if (!parent.getAttribute(elem, "command", s))
7120             return false;
7121         if (s.size()>0)
7122             command = s;
7123         if (!parent.getAttribute(elem, "file", fileName))
7124             return false;
7125             
7126         std::vector<Element *> children = elem->getChildren();
7127         for (unsigned int i=0 ; i<children.size() ; i++)
7128             {
7129             Element *child = children[i];
7130             String tagName = child->getName();
7131             if (tagName == "fileset")
7132                 {
7133                 if (!parseFileSet(child, parent, fileSet))
7134                     return false;
7135                 }
7136             }
7137         return true;
7138         }
7140 private:
7142     String command;
7143     String fileName;
7144     FileSet fileSet;
7146 };
7149 /**
7150  * Strip an executable
7151  */
7152 class TaskStrip : public Task
7154 public:
7156     TaskStrip(MakeBase &par) : Task(par)
7157         { type = TASK_STRIP; name = "strip"; }
7159     virtual ~TaskStrip()
7160         {}
7162     virtual bool execute()
7163         {
7164         String fullName = parent.resolve(fileName);
7165         //trace("fullDir:%s", fullDir.c_str());
7166         String cmd;
7167         String outbuf, errbuf;
7169         if (symFileName.size()>0)
7170             {
7171             String symFullName = parent.resolve(symFileName);
7172             cmd = "objcopy --only-keep-debug ";
7173             cmd.append(getNativePath(fullName));
7174             cmd.append(" ");
7175             cmd.append(getNativePath(symFullName));
7176             if (!executeCommand(cmd, "", outbuf, errbuf))
7177                 {
7178                 error("<strip> symbol file failed : %s", errbuf.c_str());
7179                 return false;
7180                 }
7181             }
7182             
7183         cmd = "strip ";
7184         cmd.append(getNativePath(fullName));
7185         if (!executeCommand(cmd, "", outbuf, errbuf))
7186             {
7187             error("<strip> failed : %s", errbuf.c_str());
7188             return false;
7189             }
7190         return true;
7191         }
7193     virtual bool parse(Element *elem)
7194         {
7195         if (!parent.getAttribute(elem, "file", fileName))
7196             return false;
7197         if (!parent.getAttribute(elem, "symfile", symFileName))
7198             return false;
7199         if (fileName.size() == 0)
7200             {
7201             error("<strip> requires 'file=\"fileName\"' attribute");
7202             return false;
7203             }
7204         return true;
7205         }
7207 private:
7209     String fileName;
7210     String symFileName;
7211 };
7214 /**
7215  *
7216  */
7217 class TaskTstamp : public Task
7219 public:
7221     TaskTstamp(MakeBase &par) : Task(par)
7222         { type = TASK_TSTAMP; name = "tstamp"; }
7224     virtual ~TaskTstamp()
7225         {}
7227     virtual bool execute()
7228         {
7229         return true;
7230         }
7232     virtual bool parse(Element *elem)
7233         {
7234         //trace("tstamp parse");
7235         return true;
7236         }
7237 };
7241 /**
7242  *
7243  */
7244 Task *Task::createTask(Element *elem)
7246     String tagName = elem->getName();
7247     //trace("task:%s", tagName.c_str());
7248     Task *task = NULL;
7249     if (tagName == "cc")
7250         task = new TaskCC(parent);
7251     else if (tagName == "copy")
7252         task = new TaskCopy(parent);
7253     else if (tagName == "delete")
7254         task = new TaskDelete(parent);
7255     else if (tagName == "jar")
7256         task = new TaskJar(parent);
7257     else if (tagName == "javac")
7258         task = new TaskJavac(parent);
7259     else if (tagName == "link")
7260         task = new TaskLink(parent);
7261     else if (tagName == "makefile")
7262         task = new TaskMakeFile(parent);
7263     else if (tagName == "mkdir")
7264         task = new TaskMkDir(parent);
7265     else if (tagName == "msgfmt")
7266         task = new TaskMsgFmt(parent);
7267     else if (tagName == "ranlib")
7268         task = new TaskRanlib(parent);
7269     else if (tagName == "rc")
7270         task = new TaskRC(parent);
7271     else if (tagName == "sharedlib")
7272         task = new TaskSharedLib(parent);
7273     else if (tagName == "staticlib")
7274         task = new TaskStaticLib(parent);
7275     else if (tagName == "strip")
7276         task = new TaskStrip(parent);
7277     else if (tagName == "tstamp")
7278         task = new TaskTstamp(parent);
7279     else
7280         {
7281         error("Unknown task '%s'", tagName.c_str());
7282         return NULL;
7283         }
7285     if (!task->parse(elem))
7286         {
7287         delete task;
7288         return NULL;
7289         }
7290     return task;
7295 //########################################################################
7296 //# T A R G E T
7297 //########################################################################
7299 /**
7300  *
7301  */
7302 class Target : public MakeBase
7305 public:
7307     /**
7308      *
7309      */
7310     Target(Make &par) : parent(par)
7311         { init(); }
7313     /**
7314      *
7315      */
7316     Target(const Target &other) : parent(other.parent)
7317         { init(); assign(other); }
7319     /**
7320      *
7321      */
7322     Target &operator=(const Target &other)
7323         { init(); assign(other); return *this; }
7325     /**
7326      *
7327      */
7328     virtual ~Target()
7329         { cleanup() ; }
7332     /**
7333      *
7334      */
7335     virtual Make &getParent()
7336         { return parent; }
7338     /**
7339      *
7340      */
7341     virtual String getName()
7342         { return name; }
7344     /**
7345      *
7346      */
7347     virtual void setName(const String &val)
7348         { name = val; }
7350     /**
7351      *
7352      */
7353     virtual String getDescription()
7354         { return description; }
7356     /**
7357      *
7358      */
7359     virtual void setDescription(const String &val)
7360         { description = val; }
7362     /**
7363      *
7364      */
7365     virtual void addDependency(const String &val)
7366         { deps.push_back(val); }
7368     /**
7369      *
7370      */
7371     virtual void parseDependencies(const String &val)
7372         { deps = tokenize(val, ", "); }
7374     /**
7375      *
7376      */
7377     virtual std::vector<String> &getDependencies()
7378         { return deps; }
7380     /**
7381      *
7382      */
7383     virtual String getIf()
7384         { return ifVar; }
7386     /**
7387      *
7388      */
7389     virtual void setIf(const String &val)
7390         { ifVar = val; }
7392     /**
7393      *
7394      */
7395     virtual String getUnless()
7396         { return unlessVar; }
7398     /**
7399      *
7400      */
7401     virtual void setUnless(const String &val)
7402         { unlessVar = val; }
7404     /**
7405      *
7406      */
7407     virtual void addTask(Task *val)
7408         { tasks.push_back(val); }
7410     /**
7411      *
7412      */
7413     virtual std::vector<Task *> &getTasks()
7414         { return tasks; }
7416 private:
7418     void init()
7419         {
7420         }
7422     void cleanup()
7423         {
7424         tasks.clear();
7425         }
7427     void assign(const Target &other)
7428         {
7429         //parent      = other.parent;
7430         name        = other.name;
7431         description = other.description;
7432         ifVar       = other.ifVar;
7433         unlessVar   = other.unlessVar;
7434         deps        = other.deps;
7435         tasks       = other.tasks;
7436         }
7438     Make &parent;
7440     String name;
7442     String description;
7444     String ifVar;
7446     String unlessVar;
7448     std::vector<String> deps;
7450     std::vector<Task *> tasks;
7452 };
7461 //########################################################################
7462 //# M A K E
7463 //########################################################################
7466 /**
7467  *
7468  */
7469 class Make : public MakeBase
7472 public:
7474     /**
7475      *
7476      */
7477     Make()
7478         { init(); }
7480     /**
7481      *
7482      */
7483     Make(const Make &other)
7484         { assign(other); }
7486     /**
7487      *
7488      */
7489     Make &operator=(const Make &other)
7490         { assign(other); return *this; }
7492     /**
7493      *
7494      */
7495     virtual ~Make()
7496         { cleanup(); }
7498     /**
7499      *
7500      */
7501     virtual std::map<String, Target> &getTargets()
7502         { return targets; }
7505     /**
7506      *
7507      */
7508     virtual String version()
7509         { return "BuildTool v0.6, 2006 Bob Jamison"; }
7511     /**
7512      * Overload a <property>
7513      */
7514     virtual bool specifyProperty(const String &name,
7515                                  const String &value);
7517     /**
7518      *
7519      */
7520     virtual bool run();
7522     /**
7523      *
7524      */
7525     virtual bool run(const String &target);
7529 private:
7531     /**
7532      *
7533      */
7534     void init();
7536     /**
7537      *
7538      */
7539     void cleanup();
7541     /**
7542      *
7543      */
7544     void assign(const Make &other);
7546     /**
7547      *
7548      */
7549     bool executeTask(Task &task);
7552     /**
7553      *
7554      */
7555     bool executeTarget(Target &target,
7556              std::set<String> &targetsCompleted);
7559     /**
7560      *
7561      */
7562     bool execute();
7564     /**
7565      *
7566      */
7567     bool checkTargetDependencies(Target &prop,
7568                     std::vector<String> &depList);
7570     /**
7571      *
7572      */
7573     bool parsePropertyFile(const String &fileName,
7574                            const String &prefix);
7576     /**
7577      *
7578      */
7579     bool parseProperty(Element *elem);
7581     /**
7582      *
7583      */
7584     bool parseTask(Task &task, Element *elem);
7586     /**
7587      *
7588      */
7589     bool parseFile();
7591     /**
7592      *
7593      */
7594     std::vector<String> glob(const String &pattern);
7597     //###############
7598     //# Fields
7599     //###############
7601     String projectName;
7603     String currentTarget;
7605     String defaultTarget;
7607     String specifiedTarget;
7609     String baseDir;
7611     String description;
7612     
7613     String envAlias;
7615     //std::vector<Property> properties;
7616     
7617     std::map<String, Target> targets;
7619     std::vector<Task *> allTasks;
7620     
7621     std::map<String, String> specifiedProperties;
7623 };
7626 //########################################################################
7627 //# C L A S S  M A I N T E N A N C E
7628 //########################################################################
7630 /**
7631  *
7632  */
7633 void Make::init()
7635     uri             = "build.xml";
7636     projectName     = "";
7637     currentTarget   = "";
7638     defaultTarget   = "";
7639     specifiedTarget = "";
7640     baseDir         = "";
7641     description     = "";
7642     envAlias        = "";
7643     properties.clear();
7644     for (unsigned int i = 0 ; i < allTasks.size() ; i++)
7645         delete allTasks[i];
7646     allTasks.clear();
7651 /**
7652  *
7653  */
7654 void Make::cleanup()
7656     for (unsigned int i = 0 ; i < allTasks.size() ; i++)
7657         delete allTasks[i];
7658     allTasks.clear();
7663 /**
7664  *
7665  */
7666 void Make::assign(const Make &other)
7668     uri              = other.uri;
7669     projectName      = other.projectName;
7670     currentTarget    = other.currentTarget;
7671     defaultTarget    = other.defaultTarget;
7672     specifiedTarget  = other.specifiedTarget;
7673     baseDir          = other.baseDir;
7674     description      = other.description;
7675     properties       = other.properties;
7680 //########################################################################
7681 //# U T I L I T Y    T A S K S
7682 //########################################################################
7684 /**
7685  *  Perform a file globbing
7686  */
7687 std::vector<String> Make::glob(const String &pattern)
7689     std::vector<String> res;
7690     return res;
7694 //########################################################################
7695 //# P U B L I C    A P I
7696 //########################################################################
7700 /**
7701  *
7702  */
7703 bool Make::executeTarget(Target &target,
7704              std::set<String> &targetsCompleted)
7707     String name = target.getName();
7709     //First get any dependencies for this target
7710     std::vector<String> deps = target.getDependencies();
7711     for (unsigned int i=0 ; i<deps.size() ; i++)
7712         {
7713         String dep = deps[i];
7714         //Did we do it already?  Skip
7715         if (targetsCompleted.find(dep)!=targetsCompleted.end())
7716             continue;
7717             
7718         std::map<String, Target> &tgts =
7719                target.getParent().getTargets();
7720         std::map<String, Target>::iterator iter =
7721                tgts.find(dep);
7722         if (iter == tgts.end())
7723             {
7724             error("Target '%s' dependency '%s' not found",
7725                       name.c_str(),  dep.c_str());
7726             return false;
7727             }
7728         Target depTarget = iter->second;
7729         if (!executeTarget(depTarget, targetsCompleted))
7730             {
7731             return false;
7732             }
7733         }
7735     status("## Target : %s", name.c_str());
7737     //Now let's do the tasks
7738     std::vector<Task *> &tasks = target.getTasks();
7739     for (unsigned int i=0 ; i<tasks.size() ; i++)
7740         {
7741         Task *task = tasks[i];
7742         status("---- task : %s", task->getName().c_str());
7743         if (!task->execute())
7744             {
7745             return false;
7746             }
7747         }
7748         
7749     targetsCompleted.insert(name);
7750     
7751     return true;
7756 /**
7757  *  Main execute() method.  Start here and work
7758  *  up the dependency tree 
7759  */
7760 bool Make::execute()
7762     status("######## EXECUTE");
7764     //Determine initial target
7765     if (specifiedTarget.size()>0)
7766         {
7767         currentTarget = specifiedTarget;
7768         }
7769     else if (defaultTarget.size()>0)
7770         {
7771         currentTarget = defaultTarget;
7772         }
7773     else
7774         {
7775         error("execute: no specified or default target requested");
7776         return false;
7777         }
7779     std::map<String, Target>::iterator iter =
7780                targets.find(currentTarget);
7781     if (iter == targets.end())
7782         {
7783         error("Initial target '%s' not found",
7784                  currentTarget.c_str());
7785         return false;
7786         }
7787         
7788     //Now run
7789     Target target = iter->second;
7790     std::set<String> targetsCompleted;
7791     if (!executeTarget(target, targetsCompleted))
7792         {
7793         return false;
7794         }
7796     status("######## EXECUTE COMPLETE");
7797     return true;
7803 /**
7804  *
7805  */
7806 bool Make::checkTargetDependencies(Target &target, 
7807                             std::vector<String> &depList)
7809     String tgtName = target.getName().c_str();
7810     depList.push_back(tgtName);
7812     std::vector<String> deps = target.getDependencies();
7813     for (unsigned int i=0 ; i<deps.size() ; i++)
7814         {
7815         String dep = deps[i];
7816         //First thing entered was the starting Target
7817         if (dep == depList[0])
7818             {
7819             error("Circular dependency '%s' found at '%s'",
7820                       dep.c_str(), tgtName.c_str());
7821             std::vector<String>::iterator diter;
7822             for (diter=depList.begin() ; diter!=depList.end() ; diter++)
7823                 {
7824                 error("  %s", diter->c_str());
7825                 }
7826             return false;
7827             }
7829         std::map<String, Target> &tgts =
7830                   target.getParent().getTargets();
7831         std::map<String, Target>::iterator titer = tgts.find(dep);
7832         if (titer == tgts.end())
7833             {
7834             error("Target '%s' dependency '%s' not found",
7835                       tgtName.c_str(), dep.c_str());
7836             return false;
7837             }
7838         if (!checkTargetDependencies(titer->second, depList))
7839             {
7840             return false;
7841             }
7842         }
7843     return true;
7850 static int getword(int pos, const String &inbuf, String &result)
7852     int p = pos;
7853     int len = (int)inbuf.size();
7854     String val;
7855     while (p < len)
7856         {
7857         char ch = inbuf[p];
7858         if (!isalnum(ch) && ch!='.' && ch!='_')
7859             break;
7860         val.push_back(ch);
7861         p++;
7862         }
7863     result = val;
7864     return p;
7870 /**
7871  *
7872  */
7873 bool Make::parsePropertyFile(const String &fileName,
7874                              const String &prefix)
7876     FILE *f = fopen(fileName.c_str(), "r");
7877     if (!f)
7878         {
7879         error("could not open property file %s", fileName.c_str());
7880         return false;
7881         }
7882     int linenr = 0;
7883     while (!feof(f))
7884         {
7885         char buf[256];
7886         if (!fgets(buf, 255, f))
7887             break;
7888         linenr++;
7889         String s = buf;
7890         s = trim(s);
7891         int len = s.size();
7892         if (len == 0)
7893             continue;
7894         if (s[0] == '#')
7895             continue;
7896         String key;
7897         String val;
7898         int p = 0;
7899         int p2 = getword(p, s, key);
7900         if (p2 <= p)
7901             {
7902             error("property file %s, line %d: expected keyword",
7903                     fileName.c_str(), linenr);
7904             return false;
7905             }
7906         if (prefix.size() > 0)
7907             {
7908             key.insert(0, prefix);
7909             }
7911         //skip whitespace
7912         for (p=p2 ; p<len ; p++)
7913             if (!isspace(s[p]))
7914                 break;
7916         if (p>=len || s[p]!='=')
7917             {
7918             error("property file %s, line %d: expected '='",
7919                     fileName.c_str(), linenr);
7920             return false;
7921             }
7922         p++;
7924         //skip whitespace
7925         for ( ; p<len ; p++)
7926             if (!isspace(s[p]))
7927                 break;
7929         /* This way expects a word after the =
7930         p2 = getword(p, s, val);
7931         if (p2 <= p)
7932             {
7933             error("property file %s, line %d: expected value",
7934                     fileName.c_str(), linenr);
7935             return false;
7936             }
7937         */
7938         // This way gets the rest of the line after the =
7939         if (p>=len)
7940             {
7941             error("property file %s, line %d: expected value",
7942                     fileName.c_str(), linenr);
7943             return false;
7944             }
7945         val = s.substr(p);
7946         if (key.size()==0 || val.size()==0)
7947             continue;
7949         //trace("key:'%s' val:'%s'", key.c_str(), val.c_str());
7950         //See if we wanted to overload this property
7951         std::map<String, String>::iterator iter =
7952             specifiedProperties.find(key);
7953         if (iter!=specifiedProperties.end())
7954             {
7955             val = iter->second;
7956             status("overloading property '%s' = '%s'",
7957                    key.c_str(), val.c_str());
7958             }
7959         properties[key] = val;
7960         }
7961     fclose(f);
7962     return true;
7968 /**
7969  *
7970  */
7971 bool Make::parseProperty(Element *elem)
7973     std::vector<Attribute> &attrs = elem->getAttributes();
7974     for (unsigned int i=0 ; i<attrs.size() ; i++)
7975         {
7976         String attrName = attrs[i].getName();
7977         String attrVal  = attrs[i].getValue();
7979         if (attrName == "name")
7980             {
7981             String val;
7982             if (!getAttribute(elem, "value", val))
7983                 return false;
7984             if (val.size() > 0)
7985                 {
7986                 properties[attrVal] = val;
7987                 }
7988             else
7989                 {
7990                 if (!getAttribute(elem, "location", val))
7991                     return false;
7992                 if (val.size() > 0)
7993                     {
7994                     properties[attrVal] = val;
7995                     }
7996                 }
7997             //See if we wanted to overload this property
7998             std::map<String, String>::iterator iter =
7999                 specifiedProperties.find(attrVal);
8000             if (iter != specifiedProperties.end())
8001                 {
8002                 val = iter->second;
8003                 status("overloading property '%s' = '%s'",
8004                     attrVal.c_str(), val.c_str());
8005                 properties[attrVal] = val;
8006                 }
8007             }
8008         else if (attrName == "file")
8009             {
8010             String prefix;
8011             if (!getAttribute(elem, "prefix", prefix))
8012                 return false;
8013             if (prefix.size() > 0)
8014                 {
8015                 if (prefix[prefix.size()-1] != '.')
8016                     prefix.push_back('.');
8017                 }
8018             if (!parsePropertyFile(attrName, prefix))
8019                 return false;
8020             }
8021         else if (attrName == "environment")
8022             {
8023             if (envAlias.size() > 0)
8024                 {
8025                 error("environment property can only be set once");
8026                 return false;
8027                 }
8028             envAlias = attrVal;
8029             }
8030         }
8032     return true;
8038 /**
8039  *
8040  */
8041 bool Make::parseFile()
8043     status("######## PARSE : %s", uri.getPath().c_str());
8045     Parser parser;
8046     Element *root = parser.parseFile(uri.getNativePath());
8047     if (!root)
8048         {
8049         error("Could not open %s for reading",
8050               uri.getNativePath().c_str());
8051         return false;
8052         }
8054     if (root->getChildren().size()==0 ||
8055         root->getChildren()[0]->getName()!="project")
8056         {
8057         error("Main xml element should be <project>");
8058         delete root;
8059         return false;
8060         }
8062     //########## Project attributes
8063     Element *project = root->getChildren()[0];
8064     String s = project->getAttribute("name");
8065     if (s.size() > 0)
8066         projectName = s;
8067     s = project->getAttribute("default");
8068     if (s.size() > 0)
8069         defaultTarget = s;
8070     s = project->getAttribute("basedir");
8071     if (s.size() > 0)
8072         baseDir = s;
8074     //######### PARSE MEMBERS
8075     std::vector<Element *> children = project->getChildren();
8076     for (unsigned int i=0 ; i<children.size() ; i++)
8077         {
8078         Element *elem = children[i];
8079         String tagName = elem->getName();
8081         //########## DESCRIPTION
8082         if (tagName == "description")
8083             {
8084             description = parser.trim(elem->getValue());
8085             }
8087         //######### PROPERTY
8088         else if (tagName == "property")
8089             {
8090             if (!parseProperty(elem))
8091                 return false;
8092             }
8094         //######### TARGET
8095         else if (tagName == "target")
8096             {
8097             String tname   = elem->getAttribute("name");
8098             String tdesc   = elem->getAttribute("description");
8099             String tdeps   = elem->getAttribute("depends");
8100             String tif     = elem->getAttribute("if");
8101             String tunless = elem->getAttribute("unless");
8102             Target target(*this);
8103             target.setName(tname);
8104             target.setDescription(tdesc);
8105             target.parseDependencies(tdeps);
8106             target.setIf(tif);
8107             target.setUnless(tunless);
8108             std::vector<Element *> telems = elem->getChildren();
8109             for (unsigned int i=0 ; i<telems.size() ; i++)
8110                 {
8111                 Element *telem = telems[i];
8112                 Task breeder(*this);
8113                 Task *task = breeder.createTask(telem);
8114                 if (!task)
8115                     return false;
8116                 allTasks.push_back(task);
8117                 target.addTask(task);
8118                 }
8120             //Check name
8121             if (tname.size() == 0)
8122                 {
8123                 error("no name for target");
8124                 return false;
8125                 }
8126             //Check for duplicate name
8127             if (targets.find(tname) != targets.end())
8128                 {
8129                 error("target '%s' already defined", tname.c_str());
8130                 return false;
8131                 }
8132             //more work than targets[tname]=target, but avoids default allocator
8133             targets.insert(std::make_pair<String, Target>(tname, target));
8134             }
8136         }
8138     std::map<String, Target>::iterator iter;
8139     for (iter = targets.begin() ; iter!= targets.end() ; iter++)
8140         {
8141         Target tgt = iter->second;
8142         std::vector<String> depList;
8143         if (!checkTargetDependencies(tgt, depList))
8144             {
8145             return false;
8146             }
8147         }
8150     delete root;
8151     status("######## PARSE COMPLETE");
8152     return true;
8156 /**
8157  * Overload a <property>
8158  */
8159 bool Make::specifyProperty(const String &name, const String &value)
8161     if (specifiedProperties.find(name) != specifiedProperties.end())
8162         {
8163         error("Property %s already specified", name.c_str());
8164         return false;
8165         }
8166     specifiedProperties[name] = value;
8167     return true;
8172 /**
8173  *
8174  */
8175 bool Make::run()
8177     if (!parseFile())
8178         return false;
8179         
8180     if (!execute())
8181         return false;
8183     return true;
8189 /**
8190  * Get a formatted MM:SS.sss time elapsed string
8191  */ 
8192 static String
8193 timeDiffString(struct timeval &x, struct timeval &y)
8195     long microsX  = x.tv_usec;
8196     long secondsX = x.tv_sec;
8197     long microsY  = y.tv_usec;
8198     long secondsY = y.tv_sec;
8199     if (microsX < microsY)
8200         {
8201         microsX += 1000000;
8202         secondsX -= 1;
8203         }
8205     int seconds = (int)(secondsX - secondsY);
8206     int millis  = (int)((microsX - microsY)/1000);
8208     int minutes = seconds/60;
8209     seconds += minutes*60;
8210     char buf[80];
8211     snprintf(buf, 79, "%dm %d.%03ds", minutes, seconds, millis);
8212     String ret = buf;
8213     return ret;
8214     
8217 /**
8218  *
8219  */
8220 bool Make::run(const String &target)
8222     status("####################################################");
8223     status("#   %s", version().c_str());
8224     status("####################################################");
8225     struct timeval timeStart, timeEnd;
8226     gettimeofday(&timeStart, NULL);
8227     specifiedTarget = target;
8228     if (!run())
8229         return false;
8230     gettimeofday(&timeEnd, NULL);
8231     String timeStr = timeDiffString(timeEnd, timeStart);
8232     status("####################################################");
8233     status("#   BuildTool Completed : %s", timeStr.c_str());
8234     status("####################################################");
8235     return true;
8244 }// namespace buildtool
8245 //########################################################################
8246 //# M A I N
8247 //########################################################################
8249 typedef buildtool::String String;
8251 /**
8252  *  Format an error message in printf() style
8253  */
8254 static void error(char *fmt, ...)
8256     va_list ap;
8257     va_start(ap, fmt);
8258     fprintf(stderr, "BuildTool error: ");
8259     vfprintf(stderr, fmt, ap);
8260     fprintf(stderr, "\n");
8261     va_end(ap);
8265 static bool parseProperty(const String &s, String &name, String &val)
8267     int len = s.size();
8268     int i;
8269     for (i=0 ; i<len ; i++)
8270         {
8271         char ch = s[i];
8272         if (ch == '=')
8273             break;
8274         name.push_back(ch);
8275         }
8276     if (i>=len || s[i]!='=')
8277         {
8278         error("property requires -Dname=value");
8279         return false;
8280         }
8281     i++;
8282     for ( ; i<len ; i++)
8283         {
8284         char ch = s[i];
8285         val.push_back(ch);
8286         }
8287     return true;
8291 /**
8292  * Compare a buffer with a key, for the length of the key
8293  */
8294 static bool sequ(const String &buf, char *key)
8296     int len = buf.size();
8297     for (int i=0 ; key[i] && i<len ; i++)
8298         {
8299         if (key[i] != buf[i])
8300             return false;
8301         }        
8302     return true;
8305 static void usage(int argc, char **argv)
8307     printf("usage:\n");
8308     printf("   %s [options] [target]\n", argv[0]);
8309     printf("Options:\n");
8310     printf("  -help, -h              print this message\n");
8311     printf("  -version               print the version information and exit\n");
8312     printf("  -file <file>           use given buildfile\n");
8313     printf("  -f <file>                 ''\n");
8314     printf("  -D<property>=<value>   use value for given property\n");
8320 /**
8321  * Parse the command-line args, get our options,
8322  * and run this thing
8323  */   
8324 static bool parseOptions(int argc, char **argv)
8326     if (argc < 1)
8327         {
8328         error("Cannot parse arguments");
8329         return false;
8330         }
8332     buildtool::Make make;
8334     String target;
8336     //char *progName = argv[0];
8337     for (int i=1 ; i<argc ; i++)
8338         {
8339         String arg = argv[i];
8340         if (arg.size()>1 && arg[0]=='-')
8341             {
8342             if (arg == "-h" || arg == "-help")
8343                 {
8344                 usage(argc,argv);
8345                 return true;
8346                 }
8347             else if (arg == "-version")
8348                 {
8349                 printf("%s", make.version().c_str());
8350                 return true;
8351                 }
8352             else if (arg == "-f" || arg == "-file")
8353                 {
8354                 if (i>=argc)
8355                    {
8356                    usage(argc, argv);
8357                    return false;
8358                    }
8359                 i++; //eat option
8360                 make.setURI(argv[i]);
8361                 }
8362             else if (arg.size()>2 && sequ(arg, "-D"))
8363                 {
8364                 String s = arg.substr(2, s.size());
8365                 String name, value;
8366                 if (!parseProperty(s, name, value))
8367                    {
8368                    usage(argc, argv);
8369                    return false;
8370                    }
8371                 if (!make.specifyProperty(name, value))
8372                     return false;
8373                 }
8374             else
8375                 {
8376                 error("Unknown option:%s", arg.c_str());
8377                 return false;
8378                 }
8379             }
8380         else
8381             {
8382             if (target.size()>0)
8383                 {
8384                 error("only one initial target");
8385                 usage(argc, argv);
8386                 return false;
8387                 }
8388             target = arg;
8389             }
8390         }
8392     //We have the options.  Now execute them
8393     if (!make.run(target))
8394         return false;
8396     return true;
8402 /*
8403 static bool runMake()
8405     buildtool::Make make;
8406     if (!make.run())
8407         return false;
8408     return true;
8412 static bool pkgConfigTest()
8414     buildtool::PkgConfig pkgConfig;
8415     if (!pkgConfig.readFile("gtk+-2.0.pc"))
8416         return false;
8417     return true;
8422 static bool depTest()
8424     buildtool::DepTool deptool;
8425     deptool.setSourceDirectory("/dev/ink/inkscape/src");
8426     if (!deptool.generateDependencies("build.dep"))
8427         return false;
8428     std::vector<buildtool::DepRec> res =
8429            deptool.loadDepFile("build.dep");
8430     if (res.size() == 0)
8431         return false;
8432     return true;
8435 static bool popenTest()
8437     buildtool::Make make;
8438     buildtool::String out, err;
8439     bool ret = make.executeCommand("gcc xx.cpp", "", out, err);
8440     printf("Popen test:%d '%s' '%s'\n", ret, out.c_str(), err.c_str());
8441     return true;
8445 static bool propFileTest()
8447     buildtool::Make make;
8448     make.parsePropertyFile("test.prop", "test.");
8449     return true;
8451 */
8453 int main(int argc, char **argv)
8456     if (!parseOptions(argc, argv))
8457         return 1;
8458     /*
8459     if (!popenTest())
8460         return 1;
8462     if (!depTest())
8463         return 1;
8464     if (!propFileTest())
8465         return 1;
8466     if (runMake())
8467         return 1;
8468     */
8469     return 0;
8473 //########################################################################
8474 //# E N D 
8475 //########################################################################