Code

Fix #ifdef type. Closes LP #253859
[inkscape.git] / buildtool.cpp
1 /**
2  * Simple build automation tool.
3  *
4  * Authors:
5  *   Bob Jamison
6  *   Jasper van de Gronde
7  *
8  * Copyright (C) 2006-2008 Bob Jamison
9  *
10  *  This library is free software; you can redistribute it and/or
11  *  modify it under the terms of the GNU Lesser General Public
12  *  License as published by the Free Software Foundation; either
13  *  version 2.1 of the License, or (at your option) any later version.
14  *
15  *  This library is distributed in the hope that it will be useful,
16  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  *  Lesser General Public License for more details.
19  *
20  *  You should have received a copy of the GNU Lesser General Public
21  *  License along with this library; if not, write to the Free Software
22  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
23  */
25 /**
26  * To use this file, compile with:
27  * <pre>
28  * g++ -O3 buildtool.cpp -o btool.exe
29  * (or whatever your compiler might be)
30  * Then
31  * btool
32  * or
33  * btool {target}
34  *
35  * Note: if you are using MinGW, and a not very recent version of it,
36  * gettimeofday() might be missing.  If so, just build this file with
37  * this command:
38  * g++ -O3 -DNEED_GETTIMEOFDAY buildtool.cpp -o btool.exe
39  *
40  */
42 #define BUILDTOOL_VERSION  "BuildTool v0.9.5"
44 #include <stdio.h>
45 #include <fcntl.h>
46 #include <unistd.h>
47 #include <stdarg.h>
48 #include <sys/stat.h>
49 #include <time.h>
50 #include <sys/time.h>
51 #include <utime.h>
52 #include <dirent.h>
54 #include <string>
55 #include <map>
56 #include <set>
57 #include <vector>
58 #include <algorithm>
61 #ifdef __WIN32__
62 #include <windows.h>
63 #endif
66 #include <errno.h>
69 //########################################################################
70 //# Definition of gettimeofday() for those who don't have it
71 //########################################################################
72 #ifdef NEED_GETTIMEOFDAY
73 #include <sys/timeb.h>
75 struct timezone {
76       int tz_minuteswest; /* minutes west of Greenwich */
77       int tz_dsttime;     /* type of dst correction */
78     };
80 static int gettimeofday (struct timeval *tv, struct timezone *tz)
81 {
82    struct _timeb tb;
84    if (!tv)
85       return (-1);
87     _ftime (&tb);
88     tv->tv_sec  = tb.time;
89     tv->tv_usec = tb.millitm * 1000 + 500;
90     if (tz)
91         {
92         tz->tz_minuteswest = -60 * _timezone;
93         tz->tz_dsttime = _daylight;
94         }
95     return 0;
96 }
98 #endif
106 namespace buildtool
112 //########################################################################
113 //########################################################################
114 //##  R E G E X P
115 //########################################################################
116 //########################################################################
118 /**
119  * This is the T-Rex regular expression library, which we
120  * gratefully acknowledge.  It's clean code and small size allow
121  * us to embed it in BuildTool without adding a dependency
122  *
123  */    
125 //begin trex.h
127 #ifndef _TREX_H_
128 #define _TREX_H_
129 /***************************************************************
130     T-Rex a tiny regular expression library
132     Copyright (C) 2003-2006 Alberto Demichelis
134     This software is provided 'as-is', without any express 
135     or implied warranty. In no event will the authors be held 
136     liable for any damages arising from the use of this software.
138     Permission is granted to anyone to use this software for 
139     any purpose, including commercial applications, and to alter
140     it and redistribute it freely, subject to the following restrictions:
142         1. The origin of this software must not be misrepresented;
143         you must not claim that you wrote the original software.
144         If you use this software in a product, an acknowledgment
145         in the product documentation would be appreciated but
146         is not required.
148         2. Altered source versions must be plainly marked as such,
149         and must not be misrepresented as being the original software.
151         3. This notice may not be removed or altered from any
152         source distribution.
154 ****************************************************************/
156 #ifdef _UNICODE
157 #define TRexChar unsigned short
158 #define MAX_CHAR 0xFFFF
159 #define _TREXC(c) L##c 
160 #define trex_strlen wcslen
161 #define trex_printf wprintf
162 #else
163 #define TRexChar char
164 #define MAX_CHAR 0xFF
165 #define _TREXC(c) (c) 
166 #define trex_strlen strlen
167 #define trex_printf printf
168 #endif
170 #ifndef TREX_API
171 #define TREX_API extern
172 #endif
174 #define TRex_True 1
175 #define TRex_False 0
177 typedef unsigned int TRexBool;
178 typedef struct TRex TRex;
180 typedef struct {
181     const TRexChar *begin;
182     int len;
183 } TRexMatch;
185 TREX_API TRex *trex_compile(const TRexChar *pattern,const TRexChar **error);
186 TREX_API void trex_free(TRex *exp);
187 TREX_API TRexBool trex_match(TRex* exp,const TRexChar* text);
188 TREX_API TRexBool trex_search(TRex* exp,const TRexChar* text, const TRexChar** out_begin, const TRexChar** out_end);
189 TREX_API TRexBool trex_searchrange(TRex* exp,const TRexChar* text_begin,const TRexChar* text_end,const TRexChar** out_begin, const TRexChar** out_end);
190 TREX_API int trex_getsubexpcount(TRex* exp);
191 TREX_API TRexBool trex_getsubexp(TRex* exp, int n, TRexMatch *subexp);
193 #endif
195 //end trex.h
197 //start trex.c
200 #include <stdio.h>
201 #include <string>
203 /* see copyright notice in trex.h */
204 #include <string.h>
205 #include <stdlib.h>
206 #include <ctype.h>
207 #include <setjmp.h>
208 //#include "trex.h"
210 #ifdef _UNICODE
211 #define scisprint iswprint
212 #define scstrlen wcslen
213 #define scprintf wprintf
214 #define _SC(x) L(x)
215 #else
216 #define scisprint isprint
217 #define scstrlen strlen
218 #define scprintf printf
219 #define _SC(x) (x)
220 #endif
222 #ifdef _DEBUG
223 #include <stdio.h>
225 static const TRexChar *g_nnames[] =
227     _SC("NONE"),_SC("OP_GREEDY"),    _SC("OP_OR"),
228     _SC("OP_EXPR"),_SC("OP_NOCAPEXPR"),_SC("OP_DOT"),    _SC("OP_CLASS"),
229     _SC("OP_CCLASS"),_SC("OP_NCLASS"),_SC("OP_RANGE"),_SC("OP_CHAR"),
230     _SC("OP_EOL"),_SC("OP_BOL"),_SC("OP_WB")
231 };
233 #endif
234 #define OP_GREEDY        (MAX_CHAR+1) // * + ? {n}
235 #define OP_OR            (MAX_CHAR+2)
236 #define OP_EXPR            (MAX_CHAR+3) //parentesis ()
237 #define OP_NOCAPEXPR    (MAX_CHAR+4) //parentesis (?:)
238 #define OP_DOT            (MAX_CHAR+5)
239 #define OP_CLASS        (MAX_CHAR+6)
240 #define OP_CCLASS        (MAX_CHAR+7)
241 #define OP_NCLASS        (MAX_CHAR+8) //negates class the [^
242 #define OP_RANGE        (MAX_CHAR+9)
243 #define OP_CHAR            (MAX_CHAR+10)
244 #define OP_EOL            (MAX_CHAR+11)
245 #define OP_BOL            (MAX_CHAR+12)
246 #define OP_WB            (MAX_CHAR+13)
248 #define TREX_SYMBOL_ANY_CHAR ('.')
249 #define TREX_SYMBOL_GREEDY_ONE_OR_MORE ('+')
250 #define TREX_SYMBOL_GREEDY_ZERO_OR_MORE ('*')
251 #define TREX_SYMBOL_GREEDY_ZERO_OR_ONE ('?')
252 #define TREX_SYMBOL_BRANCH ('|')
253 #define TREX_SYMBOL_END_OF_STRING ('$')
254 #define TREX_SYMBOL_BEGINNING_OF_STRING ('^')
255 #define TREX_SYMBOL_ESCAPE_CHAR ('\\')
258 typedef int TRexNodeType;
260 typedef struct tagTRexNode{
261     TRexNodeType type;
262     int left;
263     int right;
264     int next;
265 }TRexNode;
267 struct TRex{
268     const TRexChar *_eol;
269     const TRexChar *_bol;
270     const TRexChar *_p;
271     int _first;
272     int _op;
273     TRexNode *_nodes;
274     int _nallocated;
275     int _nsize;
276     int _nsubexpr;
277     TRexMatch *_matches;
278     int _currsubexp;
279     void *_jmpbuf;
280     const TRexChar **_error;
281 };
283 static int trex_list(TRex *exp);
285 static int trex_newnode(TRex *exp, TRexNodeType type)
287     TRexNode n;
288     int newid;
289     n.type = type;
290     n.next = n.right = n.left = -1;
291     if(type == OP_EXPR)
292         n.right = exp->_nsubexpr++;
293     if(exp->_nallocated < (exp->_nsize + 1)) {
294         //int oldsize = exp->_nallocated;
295         exp->_nallocated *= 2;
296         exp->_nodes = (TRexNode *)realloc(exp->_nodes, exp->_nallocated * sizeof(TRexNode));
297     }
298     exp->_nodes[exp->_nsize++] = n;
299     newid = exp->_nsize - 1;
300     return (int)newid;
303 static void trex_error(TRex *exp,const TRexChar *error)
305     if(exp->_error) *exp->_error = error;
306     longjmp(*((jmp_buf*)exp->_jmpbuf),-1);
309 static void trex_expect(TRex *exp, int n){
310     if((*exp->_p) != n) 
311         trex_error(exp, _SC("expected paren"));
312     exp->_p++;
315 static TRexChar trex_escapechar(TRex *exp)
317     if(*exp->_p == TREX_SYMBOL_ESCAPE_CHAR){
318         exp->_p++;
319         switch(*exp->_p) {
320         case 'v': exp->_p++; return '\v';
321         case 'n': exp->_p++; return '\n';
322         case 't': exp->_p++; return '\t';
323         case 'r': exp->_p++; return '\r';
324         case 'f': exp->_p++; return '\f';
325         default: return (*exp->_p++);
326         }
327     } else if(!scisprint(*exp->_p)) trex_error(exp,_SC("letter expected"));
328     return (*exp->_p++);
331 static int trex_charclass(TRex *exp,int classid)
333     int n = trex_newnode(exp,OP_CCLASS);
334     exp->_nodes[n].left = classid;
335     return n;
338 static int trex_charnode(TRex *exp,TRexBool isclass)
340     TRexChar t;
341     if(*exp->_p == TREX_SYMBOL_ESCAPE_CHAR) {
342         exp->_p++;
343         switch(*exp->_p) {
344             case 'n': exp->_p++; return trex_newnode(exp,'\n');
345             case 't': exp->_p++; return trex_newnode(exp,'\t');
346             case 'r': exp->_p++; return trex_newnode(exp,'\r');
347             case 'f': exp->_p++; return trex_newnode(exp,'\f');
348             case 'v': exp->_p++; return trex_newnode(exp,'\v');
349             case 'a': case 'A': case 'w': case 'W': case 's': case 'S': 
350             case 'd': case 'D': case 'x': case 'X': case 'c': case 'C': 
351             case 'p': case 'P': case 'l': case 'u': 
352                 {
353                 t = *exp->_p; exp->_p++; 
354                 return trex_charclass(exp,t);
355                 }
356             case 'b': 
357             case 'B':
358                 if(!isclass) {
359                     int node = trex_newnode(exp,OP_WB);
360                     exp->_nodes[node].left = *exp->_p;
361                     exp->_p++; 
362                     return node;
363                 } //else default
364             default: 
365                 t = *exp->_p; exp->_p++; 
366                 return trex_newnode(exp,t);
367         }
368     }
369     else if(!scisprint(*exp->_p)) {
370         
371         trex_error(exp,_SC("letter expected"));
372     }
373     t = *exp->_p; exp->_p++; 
374     return trex_newnode(exp,t);
376 static int trex_class(TRex *exp)
378     int ret = -1;
379     int first = -1,chain;
380     if(*exp->_p == TREX_SYMBOL_BEGINNING_OF_STRING){
381         ret = trex_newnode(exp,OP_NCLASS);
382         exp->_p++;
383     }else ret = trex_newnode(exp,OP_CLASS);
384     
385     if(*exp->_p == ']') trex_error(exp,_SC("empty class"));
386     chain = ret;
387     while(*exp->_p != ']' && exp->_p != exp->_eol) {
388         if(*exp->_p == '-' && first != -1){ 
389             int r,t;
390             if(*exp->_p++ == ']') trex_error(exp,_SC("unfinished range"));
391             r = trex_newnode(exp,OP_RANGE);
392             if(first>*exp->_p) trex_error(exp,_SC("invalid range"));
393             if(exp->_nodes[first].type == OP_CCLASS) trex_error(exp,_SC("cannot use character classes in ranges"));
394             exp->_nodes[r].left = exp->_nodes[first].type;
395             t = trex_escapechar(exp);
396             exp->_nodes[r].right = t;
397             exp->_nodes[chain].next = r;
398             chain = r;
399             first = -1;
400         }
401         else{
402             if(first!=-1){
403                 int c = first;
404                 exp->_nodes[chain].next = c;
405                 chain = c;
406                 first = trex_charnode(exp,TRex_True);
407             }
408             else{
409                 first = trex_charnode(exp,TRex_True);
410             }
411         }
412     }
413     if(first!=-1){
414         int c = first;
415         exp->_nodes[chain].next = c;
416         chain = c;
417         first = -1;
418     }
419     /* hack? */
420     exp->_nodes[ret].left = exp->_nodes[ret].next;
421     exp->_nodes[ret].next = -1;
422     return ret;
425 static int trex_parsenumber(TRex *exp)
427     int ret = *exp->_p-'0';
428     int positions = 10;
429     exp->_p++;
430     while(isdigit(*exp->_p)) {
431         ret = ret*10+(*exp->_p++-'0');
432         if(positions==1000000000) trex_error(exp,_SC("overflow in numeric constant"));
433         positions *= 10;
434     };
435     return ret;
438 static int trex_element(TRex *exp)
440     int ret = -1;
441     switch(*exp->_p)
442     {
443     case '(': {
444         int expr,newn;
445         exp->_p++;
448         if(*exp->_p =='?') {
449             exp->_p++;
450             trex_expect(exp,':');
451             expr = trex_newnode(exp,OP_NOCAPEXPR);
452         }
453         else
454             expr = trex_newnode(exp,OP_EXPR);
455         newn = trex_list(exp);
456         exp->_nodes[expr].left = newn;
457         ret = expr;
458         trex_expect(exp,')');
459               }
460               break;
461     case '[':
462         exp->_p++;
463         ret = trex_class(exp);
464         trex_expect(exp,']');
465         break;
466     case TREX_SYMBOL_END_OF_STRING: exp->_p++; ret = trex_newnode(exp,OP_EOL);break;
467     case TREX_SYMBOL_ANY_CHAR: exp->_p++; ret = trex_newnode(exp,OP_DOT);break;
468     default:
469         ret = trex_charnode(exp,TRex_False);
470         break;
471     }
473     {
474         int op;
475         TRexBool isgreedy = TRex_False;
476         unsigned short p0 = 0, p1 = 0;
477         switch(*exp->_p){
478             case TREX_SYMBOL_GREEDY_ZERO_OR_MORE: p0 = 0; p1 = 0xFFFF; exp->_p++; isgreedy = TRex_True; break;
479             case TREX_SYMBOL_GREEDY_ONE_OR_MORE: p0 = 1; p1 = 0xFFFF; exp->_p++; isgreedy = TRex_True; break;
480             case TREX_SYMBOL_GREEDY_ZERO_OR_ONE: p0 = 0; p1 = 1; exp->_p++; isgreedy = TRex_True; break;
481             case '{':
482                 exp->_p++;
483                 if(!isdigit(*exp->_p)) trex_error(exp,_SC("number expected"));
484                 p0 = (unsigned short)trex_parsenumber(exp);
485                 /*******************************/
486                 switch(*exp->_p) {
487             case '}':
488                 p1 = p0; exp->_p++;
489                 break;
490             case ',':
491                 exp->_p++;
492                 p1 = 0xFFFF;
493                 if(isdigit(*exp->_p)){
494                     p1 = (unsigned short)trex_parsenumber(exp);
495                 }
496                 trex_expect(exp,'}');
497                 break;
498             default:
499                 trex_error(exp,_SC(", or } expected"));
500         }
501         /*******************************/
502         isgreedy = TRex_True; 
503         break;
505         }
506         if(isgreedy) {
507             int nnode = trex_newnode(exp,OP_GREEDY);
508             op = OP_GREEDY;
509             exp->_nodes[nnode].left = ret;
510             exp->_nodes[nnode].right = ((p0)<<16)|p1;
511             ret = nnode;
512         }
513     }
514     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')) {
515         int nnode = trex_element(exp);
516         exp->_nodes[ret].next = nnode;
517     }
519     return ret;
522 static int trex_list(TRex *exp)
524     int ret=-1,e;
525     if(*exp->_p == TREX_SYMBOL_BEGINNING_OF_STRING) {
526         exp->_p++;
527         ret = trex_newnode(exp,OP_BOL);
528     }
529     e = trex_element(exp);
530     if(ret != -1) {
531         exp->_nodes[ret].next = e;
532     }
533     else ret = e;
535     if(*exp->_p == TREX_SYMBOL_BRANCH) {
536         int temp,tright;
537         exp->_p++;
538         temp = trex_newnode(exp,OP_OR);
539         exp->_nodes[temp].left = ret;
540         tright = trex_list(exp);
541         exp->_nodes[temp].right = tright;
542         ret = temp;
543     }
544     return ret;
547 static TRexBool trex_matchcclass(int cclass,TRexChar c)
549     switch(cclass) {
550     case 'a': return isalpha(c)?TRex_True:TRex_False;
551     case 'A': return !isalpha(c)?TRex_True:TRex_False;
552     case 'w': return (isalnum(c) || c == '_')?TRex_True:TRex_False;
553     case 'W': return (!isalnum(c) && c != '_')?TRex_True:TRex_False;
554     case 's': return isspace(c)?TRex_True:TRex_False;
555     case 'S': return !isspace(c)?TRex_True:TRex_False;
556     case 'd': return isdigit(c)?TRex_True:TRex_False;
557     case 'D': return !isdigit(c)?TRex_True:TRex_False;
558     case 'x': return isxdigit(c)?TRex_True:TRex_False;
559     case 'X': return !isxdigit(c)?TRex_True:TRex_False;
560     case 'c': return iscntrl(c)?TRex_True:TRex_False;
561     case 'C': return !iscntrl(c)?TRex_True:TRex_False;
562     case 'p': return ispunct(c)?TRex_True:TRex_False;
563     case 'P': return !ispunct(c)?TRex_True:TRex_False;
564     case 'l': return islower(c)?TRex_True:TRex_False;
565     case 'u': return isupper(c)?TRex_True:TRex_False;
566     }
567     return TRex_False; /*cannot happen*/
570 static TRexBool trex_matchclass(TRex* exp,TRexNode *node,TRexChar c)
572     do {
573         switch(node->type) {
574             case OP_RANGE:
575                 if(c >= node->left && c <= node->right) return TRex_True;
576                 break;
577             case OP_CCLASS:
578                 if(trex_matchcclass(node->left,c)) return TRex_True;
579                 break;
580             default:
581                 if(c == node->type)return TRex_True;
582         }
583     } while((node->next != -1) && (node = &exp->_nodes[node->next]));
584     return TRex_False;
587 static const TRexChar *trex_matchnode(TRex* exp,TRexNode *node,const TRexChar *str,TRexNode *next)
589     
590     TRexNodeType type = node->type;
591     switch(type) {
592     case OP_GREEDY: {
593         //TRexNode *greedystop = (node->next != -1) ? &exp->_nodes[node->next] : NULL;
594         TRexNode *greedystop = NULL;
595         int p0 = (node->right >> 16)&0x0000FFFF, p1 = node->right&0x0000FFFF, nmaches = 0;
596         const TRexChar *s=str, *good = str;
598         if(node->next != -1) {
599             greedystop = &exp->_nodes[node->next];
600         }
601         else {
602             greedystop = next;
603         }
605         while((nmaches == 0xFFFF || nmaches < p1)) {
607             const TRexChar *stop;
608             if(!(s = trex_matchnode(exp,&exp->_nodes[node->left],s,greedystop)))
609                 break;
610             nmaches++;
611             good=s;
612             if(greedystop) {
613                 //checks that 0 matches satisfy the expression(if so skips)
614                 //if not would always stop(for instance if is a '?')
615                 if(greedystop->type != OP_GREEDY ||
616                 (greedystop->type == OP_GREEDY && ((greedystop->right >> 16)&0x0000FFFF) != 0))
617                 {
618                     TRexNode *gnext = NULL;
619                     if(greedystop->next != -1) {
620                         gnext = &exp->_nodes[greedystop->next];
621                     }else if(next && next->next != -1){
622                         gnext = &exp->_nodes[next->next];
623                     }
624                     stop = trex_matchnode(exp,greedystop,s,gnext);
625                     if(stop) {
626                         //if satisfied stop it
627                         if(p0 == p1 && p0 == nmaches) break;
628                         else if(nmaches >= p0 && p1 == 0xFFFF) break;
629                         else if(nmaches >= p0 && nmaches <= p1) break;
630                     }
631                 }
632             }
633             
634             if(s >= exp->_eol)
635                 break;
636         }
637         if(p0 == p1 && p0 == nmaches) return good;
638         else if(nmaches >= p0 && p1 == 0xFFFF) return good;
639         else if(nmaches >= p0 && nmaches <= p1) return good;
640         return NULL;
641     }
642     case OP_OR: {
643             const TRexChar *asd = str;
644             TRexNode *temp=&exp->_nodes[node->left];
645             while( (asd = trex_matchnode(exp,temp,asd,NULL)) ) {
646                 if(temp->next != -1)
647                     temp = &exp->_nodes[temp->next];
648                 else
649                     return asd;
650             }
651             asd = str;
652             temp = &exp->_nodes[node->right];
653             while( (asd = trex_matchnode(exp,temp,asd,NULL)) ) {
654                 if(temp->next != -1)
655                     temp = &exp->_nodes[temp->next];
656                 else
657                     return asd;
658             }
659             return NULL;
660             break;
661     }
662     case OP_EXPR:
663     case OP_NOCAPEXPR:{
664             TRexNode *n = &exp->_nodes[node->left];
665             const TRexChar *cur = str;
666             int capture = -1;
667             if(node->type != OP_NOCAPEXPR && node->right == exp->_currsubexp) {
668                 capture = exp->_currsubexp;
669                 exp->_matches[capture].begin = cur;
670                 exp->_currsubexp++;
671             }
672             
673             do {
674                 TRexNode *subnext = NULL;
675                 if(n->next != -1) {
676                     subnext = &exp->_nodes[n->next];
677                 }else {
678                     subnext = next;
679                 }
680                 if(!(cur = trex_matchnode(exp,n,cur,subnext))) {
681                     if(capture != -1){
682                         exp->_matches[capture].begin = 0;
683                         exp->_matches[capture].len = 0;
684                     }
685                     return NULL;
686                 }
687             } while((n->next != -1) && (n = &exp->_nodes[n->next]));
689             if(capture != -1) 
690                 exp->_matches[capture].len = cur - exp->_matches[capture].begin;
691             return cur;
692     }                 
693     case OP_WB:
694         if((str == exp->_bol && !isspace(*str))
695          || (str == exp->_eol && !isspace(*(str-1)))
696          || (!isspace(*str) && isspace(*(str+1)))
697          || (isspace(*str) && !isspace(*(str+1))) ) {
698             return (node->left == 'b')?str:NULL;
699         }
700         return (node->left == 'b')?NULL:str;
701     case OP_BOL:
702         if(str == exp->_bol) return str;
703         return NULL;
704     case OP_EOL:
705         if(str == exp->_eol) return str;
706         return NULL;
707     case OP_DOT:{
708         *str++;
709                 }
710         return str;
711     case OP_NCLASS:
712     case OP_CLASS:
713         if(trex_matchclass(exp,&exp->_nodes[node->left],*str)?(type == OP_CLASS?TRex_True:TRex_False):(type == OP_NCLASS?TRex_True:TRex_False)) {
714             *str++;
715             return str;
716         }
717         return NULL;
718     case OP_CCLASS:
719         if(trex_matchcclass(node->left,*str)) {
720             *str++;
721             return str;
722         }
723         return NULL;
724     default: /* char */
725         if(*str != node->type) return NULL;
726         *str++;
727         return str;
728     }
729     return NULL;
732 /* public api */
733 TRex *trex_compile(const TRexChar *pattern,const TRexChar **error)
735     TRex *exp = (TRex *)malloc(sizeof(TRex));
736     exp->_eol = exp->_bol = NULL;
737     exp->_p = pattern;
738     exp->_nallocated = (int)scstrlen(pattern) * sizeof(TRexChar);
739     exp->_nodes = (TRexNode *)malloc(exp->_nallocated * sizeof(TRexNode));
740     exp->_nsize = 0;
741     exp->_matches = 0;
742     exp->_nsubexpr = 0;
743     exp->_first = trex_newnode(exp,OP_EXPR);
744     exp->_error = error;
745     exp->_jmpbuf = malloc(sizeof(jmp_buf));
746     if(setjmp(*((jmp_buf*)exp->_jmpbuf)) == 0) {
747         int res = trex_list(exp);
748         exp->_nodes[exp->_first].left = res;
749         if(*exp->_p!='\0')
750             trex_error(exp,_SC("unexpected character"));
751 #ifdef _DEBUG
752         {
753             int nsize,i;
754             TRexNode *t;
755             nsize = exp->_nsize;
756             t = &exp->_nodes[0];
757             scprintf(_SC("\n"));
758             for(i = 0;i < nsize; i++) {
759                 if(exp->_nodes[i].type>MAX_CHAR)
760                     scprintf(_SC("[%02d] %10s "),i,g_nnames[exp->_nodes[i].type-MAX_CHAR]);
761                 else
762                     scprintf(_SC("[%02d] %10c "),i,exp->_nodes[i].type);
763                 scprintf(_SC("left %02d right %02d next %02d\n"),exp->_nodes[i].left,exp->_nodes[i].right,exp->_nodes[i].next);
764             }
765             scprintf(_SC("\n"));
766         }
767 #endif
768         exp->_matches = (TRexMatch *) malloc(exp->_nsubexpr * sizeof(TRexMatch));
769         memset(exp->_matches,0,exp->_nsubexpr * sizeof(TRexMatch));
770     }
771     else{
772         trex_free(exp);
773         return NULL;
774     }
775     return exp;
778 void trex_free(TRex *exp)
780     if(exp)    {
781         if(exp->_nodes) free(exp->_nodes);
782         if(exp->_jmpbuf) free(exp->_jmpbuf);
783         if(exp->_matches) free(exp->_matches);
784         free(exp);
785     }
788 TRexBool trex_match(TRex* exp,const TRexChar* text)
790     const TRexChar* res = NULL;
791     exp->_bol = text;
792     exp->_eol = text + scstrlen(text);
793     exp->_currsubexp = 0;
794     res = trex_matchnode(exp,exp->_nodes,text,NULL);
795     if(res == NULL || res != exp->_eol)
796         return TRex_False;
797     return TRex_True;
800 TRexBool trex_searchrange(TRex* exp,const TRexChar* text_begin,const TRexChar* text_end,const TRexChar** out_begin, const TRexChar** out_end)
802     const TRexChar *cur = NULL;
803     int node = exp->_first;
804     if(text_begin >= text_end) return TRex_False;
805     exp->_bol = text_begin;
806     exp->_eol = text_end;
807     do {
808         cur = text_begin;
809         while(node != -1) {
810             exp->_currsubexp = 0;
811             cur = trex_matchnode(exp,&exp->_nodes[node],cur,NULL);
812             if(!cur)
813                 break;
814             node = exp->_nodes[node].next;
815         }
816         *text_begin++;
817     } while(cur == NULL && text_begin != text_end);
819     if(cur == NULL)
820         return TRex_False;
822     --text_begin;
824     if(out_begin) *out_begin = text_begin;
825     if(out_end) *out_end = cur;
826     return TRex_True;
829 TRexBool trex_search(TRex* exp,const TRexChar* text, const TRexChar** out_begin, const TRexChar** out_end)
831     return trex_searchrange(exp,text,text + scstrlen(text),out_begin,out_end);
834 int trex_getsubexpcount(TRex* exp)
836     return exp->_nsubexpr;
839 TRexBool trex_getsubexp(TRex* exp, int n, TRexMatch *subexp)
841     if( n<0 || n >= exp->_nsubexpr) return TRex_False;
842     *subexp = exp->_matches[n];
843     return TRex_True;
847 //########################################################################
848 //########################################################################
849 //##  E N D    R E G E X P
850 //########################################################################
851 //########################################################################
857 //########################################################################
858 //########################################################################
859 //##  X M L
860 //########################################################################
861 //########################################################################
863 // Note:  This mini-dom library comes from Pedro, another little project
864 // of mine.
866 typedef std::string String;
867 typedef unsigned int XMLCh;
870 class Namespace
872 public:
873     Namespace()
874         {}
876     Namespace(const String &prefixArg, const String &namespaceURIArg)
877         {
878         prefix       = prefixArg;
879         namespaceURI = namespaceURIArg;
880         }
882     Namespace(const Namespace &other)
883         {
884         assign(other);
885         }
887     Namespace &operator=(const Namespace &other)
888         {
889         assign(other);
890         return *this;
891         }
893     virtual ~Namespace()
894         {}
896     virtual String getPrefix()
897         { return prefix; }
899     virtual String getNamespaceURI()
900         { return namespaceURI; }
902 protected:
904     void assign(const Namespace &other)
905         {
906         prefix       = other.prefix;
907         namespaceURI = other.namespaceURI;
908         }
910     String prefix;
911     String namespaceURI;
913 };
915 class Attribute
917 public:
918     Attribute()
919         {}
921     Attribute(const String &nameArg, const String &valueArg)
922         {
923         name  = nameArg;
924         value = valueArg;
925         }
927     Attribute(const Attribute &other)
928         {
929         assign(other);
930         }
932     Attribute &operator=(const Attribute &other)
933         {
934         assign(other);
935         return *this;
936         }
938     virtual ~Attribute()
939         {}
941     virtual String getName()
942         { return name; }
944     virtual String getValue()
945         { return value; }
947 protected:
949     void assign(const Attribute &other)
950         {
951         name  = other.name;
952         value = other.value;
953         }
955     String name;
956     String value;
958 };
961 class Element
963 friend class Parser;
965 public:
966     Element()
967         {
968         init();
969         }
971     Element(const String &nameArg)
972         {
973         init();
974         name   = nameArg;
975         }
977     Element(const String &nameArg, const String &valueArg)
978         {
979         init();
980         name   = nameArg;
981         value  = valueArg;
982         }
984     Element(const Element &other)
985         {
986         assign(other);
987         }
989     Element &operator=(const Element &other)
990         {
991         assign(other);
992         return *this;
993         }
995     virtual Element *clone();
997     virtual ~Element()
998         {
999         for (unsigned int i=0 ; i<children.size() ; i++)
1000             delete children[i];
1001         }
1003     virtual String getName()
1004         { return name; }
1006     virtual String getValue()
1007         { return value; }
1009     Element *getParent()
1010         { return parent; }
1012     std::vector<Element *> getChildren()
1013         { return children; }
1015     std::vector<Element *> findElements(const String &name);
1017     String getAttribute(const String &name);
1019     std::vector<Attribute> &getAttributes()
1020         { return attributes; } 
1022     String getTagAttribute(const String &tagName, const String &attrName);
1024     String getTagValue(const String &tagName);
1026     void addChild(Element *child);
1028     void addAttribute(const String &name, const String &value);
1030     void addNamespace(const String &prefix, const String &namespaceURI);
1033     /**
1034      * Prettyprint an XML tree to an output stream.  Elements are indented
1035      * according to element hierarchy.
1036      * @param f a stream to receive the output
1037      * @param elem the element to output
1038      */
1039     void writeIndented(FILE *f);
1041     /**
1042      * Prettyprint an XML tree to standard output.  This is the equivalent of
1043      * writeIndented(stdout).
1044      * @param elem the element to output
1045      */
1046     void print();
1047     
1048     int getLine()
1049         { return line; }
1051 protected:
1053     void init()
1054         {
1055         parent = NULL;
1056         line   = 0;
1057         }
1059     void assign(const Element &other)
1060         {
1061         parent     = other.parent;
1062         children   = other.children;
1063         attributes = other.attributes;
1064         namespaces = other.namespaces;
1065         name       = other.name;
1066         value      = other.value;
1067         line       = other.line;
1068         }
1070     void findElementsRecursive(std::vector<Element *>&res, const String &name);
1072     void writeIndentedRecursive(FILE *f, int indent);
1074     Element *parent;
1076     std::vector<Element *>children;
1078     std::vector<Attribute> attributes;
1079     std::vector<Namespace> namespaces;
1081     String name;
1082     String value;
1083     
1084     int line;
1085 };
1091 class Parser
1093 public:
1094     /**
1095      * Constructor
1096      */
1097     Parser()
1098         { init(); }
1100     virtual ~Parser()
1101         {}
1103     /**
1104      * Parse XML in a char buffer.
1105      * @param buf a character buffer to parse
1106      * @param pos position to start parsing
1107      * @param len number of chars, from pos, to parse.
1108      * @return a pointer to the root of the XML document;
1109      */
1110     Element *parse(const char *buf,int pos,int len);
1112     /**
1113      * Parse XML in a char buffer.
1114      * @param buf a character buffer to parse
1115      * @param pos position to start parsing
1116      * @param len number of chars, from pos, to parse.
1117      * @return a pointer to the root of the XML document;
1118      */
1119     Element *parse(const String &buf);
1121     /**
1122      * Parse a named XML file.  The file is loaded like a data file;
1123      * the original format is not preserved.
1124      * @param fileName the name of the file to read
1125      * @return a pointer to the root of the XML document;
1126      */
1127     Element *parseFile(const String &fileName);
1129     /**
1130      * Utility method to preprocess a string for XML
1131      * output, escaping its entities.
1132      * @param str the string to encode
1133      */
1134     static String encode(const String &str);
1136     /**
1137      *  Removes whitespace from beginning and end of a string
1138      */
1139     String trim(const String &s);
1141 private:
1143     void init()
1144         {
1145         keepGoing       = true;
1146         currentNode     = NULL;
1147         parselen        = 0;
1148         parsebuf        = NULL;
1149         currentPosition = 0;
1150         }
1152     int countLines(int begin, int end);
1154     void getLineAndColumn(int pos, int *lineNr, int *colNr);
1156     void error(const char *fmt, ...);
1158     int peek(int pos);
1160     int match(int pos, const char *text);
1162     int skipwhite(int p);
1164     int getWord(int p0, String &buf);
1166     int getQuoted(int p0, String &buf, int do_i_parse);
1168     int parseVersion(int p0);
1170     int parseDoctype(int p0);
1172     int parseElement(int p0, Element *par,int depth);
1174     Element *parse(XMLCh *buf,int pos,int len);
1176     bool       keepGoing;
1177     Element    *currentNode;
1178     int        parselen;
1179     XMLCh      *parsebuf;
1180     String     cdatabuf;
1181     int        currentPosition;
1182 };
1187 //########################################################################
1188 //# E L E M E N T
1189 //########################################################################
1191 Element *Element::clone()
1193     Element *elem = new Element(name, value);
1194     elem->parent     = parent;
1195     elem->attributes = attributes;
1196     elem->namespaces = namespaces;
1197     elem->line       = line;
1199     std::vector<Element *>::iterator iter;
1200     for (iter = children.begin(); iter != children.end() ; iter++)
1201         {
1202         elem->addChild((*iter)->clone());
1203         }
1204     return elem;
1208 void Element::findElementsRecursive(std::vector<Element *>&res, const String &name)
1210     if (getName() == name)
1211         {
1212         res.push_back(this);
1213         }
1214     for (unsigned int i=0; i<children.size() ; i++)
1215         children[i]->findElementsRecursive(res, name);
1218 std::vector<Element *> Element::findElements(const String &name)
1220     std::vector<Element *> res;
1221     findElementsRecursive(res, name);
1222     return res;
1225 String Element::getAttribute(const String &name)
1227     for (unsigned int i=0 ; i<attributes.size() ; i++)
1228         if (attributes[i].getName() ==name)
1229             return attributes[i].getValue();
1230     return "";
1233 String Element::getTagAttribute(const String &tagName, const String &attrName)
1235     std::vector<Element *>elems = findElements(tagName);
1236     if (elems.size() <1)
1237         return "";
1238     String res = elems[0]->getAttribute(attrName);
1239     return res;
1242 String Element::getTagValue(const String &tagName)
1244     std::vector<Element *>elems = findElements(tagName);
1245     if (elems.size() <1)
1246         return "";
1247     String res = elems[0]->getValue();
1248     return res;
1251 void Element::addChild(Element *child)
1253     if (!child)
1254         return;
1255     child->parent = this;
1256     children.push_back(child);
1260 void Element::addAttribute(const String &name, const String &value)
1262     Attribute attr(name, value);
1263     attributes.push_back(attr);
1266 void Element::addNamespace(const String &prefix, const String &namespaceURI)
1268     Namespace ns(prefix, namespaceURI);
1269     namespaces.push_back(ns);
1272 void Element::writeIndentedRecursive(FILE *f, int indent)
1274     int i;
1275     if (!f)
1276         return;
1277     //Opening tag, and attributes
1278     for (i=0;i<indent;i++)
1279         fputc(' ',f);
1280     fprintf(f,"<%s",name.c_str());
1281     for (unsigned int i=0 ; i<attributes.size() ; i++)
1282         {
1283         fprintf(f," %s=\"%s\"",
1284               attributes[i].getName().c_str(),
1285               attributes[i].getValue().c_str());
1286         }
1287     for (unsigned int i=0 ; i<namespaces.size() ; i++)
1288         {
1289         fprintf(f," xmlns:%s=\"%s\"",
1290               namespaces[i].getPrefix().c_str(),
1291               namespaces[i].getNamespaceURI().c_str());
1292         }
1293     fprintf(f,">\n");
1295     //Between the tags
1296     if (value.size() > 0)
1297         {
1298         for (int i=0;i<indent;i++)
1299             fputc(' ', f);
1300         fprintf(f," %s\n", value.c_str());
1301         }
1303     for (unsigned int i=0 ; i<children.size() ; i++)
1304         children[i]->writeIndentedRecursive(f, indent+2);
1306     //Closing tag
1307     for (int i=0; i<indent; i++)
1308         fputc(' ',f);
1309     fprintf(f,"</%s>\n", name.c_str());
1312 void Element::writeIndented(FILE *f)
1314     writeIndentedRecursive(f, 0);
1317 void Element::print()
1319     writeIndented(stdout);
1323 //########################################################################
1324 //# P A R S E R
1325 //########################################################################
1329 typedef struct
1330     {
1331     const char *escaped;
1332     char value;
1333     } EntityEntry;
1335 static EntityEntry entities[] =
1337     { "&amp;" , '&'  },
1338     { "&lt;"  , '<'  },
1339     { "&gt;"  , '>'  },
1340     { "&apos;", '\'' },
1341     { "&quot;", '"'  },
1342     { NULL    , '\0' }
1343 };
1347 /**
1348  *  Removes whitespace from beginning and end of a string
1349  */
1350 String Parser::trim(const String &s)
1352     if (s.size() < 1)
1353         return s;
1354     
1355     //Find first non-ws char
1356     unsigned int begin = 0;
1357     for ( ; begin < s.size() ; begin++)
1358         {
1359         if (!isspace(s[begin]))
1360             break;
1361         }
1363     //Find first non-ws char, going in reverse
1364     unsigned int end = s.size() - 1;
1365     for ( ; end > begin ; end--)
1366         {
1367         if (!isspace(s[end]))
1368             break;
1369         }
1370     //trace("begin:%d  end:%d", begin, end);
1372     String res = s.substr(begin, end-begin+1);
1373     return res;
1377 int Parser::countLines(int begin, int end)
1379     int count = 0;
1380     for (int i=begin ; i<end ; i++)
1381         {
1382         XMLCh ch = parsebuf[i];
1383         if (ch == '\n' || ch == '\r')
1384             count++;
1385         }
1386     return count;
1390 void Parser::getLineAndColumn(int pos, int *lineNr, int *colNr)
1392     int line = 1;
1393     int col  = 1;
1394     for (long i=0 ; i<pos ; i++)
1395         {
1396         XMLCh ch = parsebuf[i];
1397         if (ch == '\n' || ch == '\r')
1398             {
1399             col = 0;
1400             line ++;
1401             }
1402         else
1403             col++;
1404         }
1405     *lineNr = line;
1406     *colNr  = col;
1411 void Parser::error(const char *fmt, ...)
1413     int lineNr;
1414     int colNr;
1415     getLineAndColumn(currentPosition, &lineNr, &colNr);
1416     va_list args;
1417     fprintf(stderr, "xml error at line %d, column %d:", lineNr, colNr);
1418     va_start(args,fmt);
1419     vfprintf(stderr,fmt,args);
1420     va_end(args) ;
1421     fprintf(stderr, "\n");
1426 int Parser::peek(int pos)
1428     if (pos >= parselen)
1429         return -1;
1430     currentPosition = pos;
1431     int ch = parsebuf[pos];
1432     //printf("ch:%c\n", ch);
1433     return ch;
1438 String Parser::encode(const String &str)
1440     String ret;
1441     for (unsigned int i=0 ; i<str.size() ; i++)
1442         {
1443         XMLCh ch = (XMLCh)str[i];
1444         if (ch == '&')
1445             ret.append("&amp;");
1446         else if (ch == '<')
1447             ret.append("&lt;");
1448         else if (ch == '>')
1449             ret.append("&gt;");
1450         else if (ch == '\'')
1451             ret.append("&apos;");
1452         else if (ch == '"')
1453             ret.append("&quot;");
1454         else
1455             ret.push_back(ch);
1457         }
1458     return ret;
1462 int Parser::match(int p0, const char *text)
1464     int p = p0;
1465     while (*text)
1466         {
1467         if (peek(p) != *text)
1468             return p0;
1469         p++; text++;
1470         }
1471     return p;
1476 int Parser::skipwhite(int p)
1479     while (p<parselen)
1480         {
1481         int p2 = match(p, "<!--");
1482         if (p2 > p)
1483             {
1484             p = p2;
1485             while (p<parselen)
1486               {
1487               p2 = match(p, "-->");
1488               if (p2 > p)
1489                   {
1490                   p = p2;
1491                   break;
1492                   }
1493               p++;
1494               }
1495           }
1496       XMLCh b = peek(p);
1497       if (!isspace(b))
1498           break;
1499       p++;
1500       }
1501   return p;
1504 /* modify this to allow all chars for an element or attribute name*/
1505 int Parser::getWord(int p0, String &buf)
1507     int p = p0;
1508     while (p<parselen)
1509         {
1510         XMLCh b = peek(p);
1511         if (b<=' ' || b=='/' || b=='>' || b=='=')
1512             break;
1513         buf.push_back(b);
1514         p++;
1515         }
1516     return p;
1519 int Parser::getQuoted(int p0, String &buf, int do_i_parse)
1522     int p = p0;
1523     if (peek(p) != '"' && peek(p) != '\'')
1524         return p0;
1525     p++;
1527     while ( p<parselen )
1528         {
1529         XMLCh b = peek(p);
1530         if (b=='"' || b=='\'')
1531             break;
1532         if (b=='&' && do_i_parse)
1533             {
1534             bool found = false;
1535             for (EntityEntry *ee = entities ; ee->value ; ee++)
1536                 {
1537                 int p2 = match(p, ee->escaped);
1538                 if (p2>p)
1539                     {
1540                     buf.push_back(ee->value);
1541                     p = p2;
1542                     found = true;
1543                     break;
1544                     }
1545                 }
1546             if (!found)
1547                 {
1548                 error("unterminated entity");
1549                 return false;
1550                 }
1551             }
1552         else
1553             {
1554             buf.push_back(b);
1555             p++;
1556             }
1557         }
1558     return p;
1561 int Parser::parseVersion(int p0)
1563     //printf("### parseVersion: %d\n", p0);
1565     int p = p0;
1567     p = skipwhite(p0);
1569     if (peek(p) != '<')
1570         return p0;
1572     p++;
1573     if (p>=parselen || peek(p)!='?')
1574         return p0;
1576     p++;
1578     String buf;
1580     while (p<parselen)
1581         {
1582         XMLCh ch = peek(p);
1583         if (ch=='?')
1584             {
1585             p++;
1586             break;
1587             }
1588         buf.push_back(ch);
1589         p++;
1590         }
1592     if (peek(p) != '>')
1593         return p0;
1594     p++;
1596     //printf("Got version:%s\n",buf.c_str());
1597     return p;
1600 int Parser::parseDoctype(int p0)
1602     //printf("### parseDoctype: %d\n", p0);
1604     int p = p0;
1605     p = skipwhite(p);
1607     if (p>=parselen || peek(p)!='<')
1608         return p0;
1610     p++;
1612     if (peek(p)!='!' || peek(p+1)=='-')
1613         return p0;
1614     p++;
1616     String buf;
1617     while (p<parselen)
1618         {
1619         XMLCh ch = peek(p);
1620         if (ch=='>')
1621             {
1622             p++;
1623             break;
1624             }
1625         buf.push_back(ch);
1626         p++;
1627         }
1629     //printf("Got doctype:%s\n",buf.c_str());
1630     return p;
1635 int Parser::parseElement(int p0, Element *par,int lineNr)
1638     int p = p0;
1640     int p2 = p;
1642     p = skipwhite(p);
1644     //## Get open tag
1645     XMLCh ch = peek(p);
1646     if (ch!='<')
1647         return p0;
1649     //int line, col;
1650     //getLineAndColumn(p, &line, &col);
1652     p++;
1654     String openTagName;
1655     p = skipwhite(p);
1656     p = getWord(p, openTagName);
1657     //printf("####tag :%s\n", openTagName.c_str());
1658     p = skipwhite(p);
1660     //Add element to tree
1661     Element *n = new Element(openTagName);
1662     n->line = lineNr + countLines(p0, p);
1663     n->parent = par;
1664     par->addChild(n);
1666     // Get attributes
1667     if (peek(p) != '>')
1668         {
1669         while (p<parselen)
1670             {
1671             p = skipwhite(p);
1672             ch = peek(p);
1673             //printf("ch:%c\n",ch);
1674             if (ch=='>')
1675                 break;
1676             else if (ch=='/' && p<parselen+1)
1677                 {
1678                 p++;
1679                 p = skipwhite(p);
1680                 ch = peek(p);
1681                 if (ch=='>')
1682                     {
1683                     p++;
1684                     //printf("quick close\n");
1685                     return p;
1686                     }
1687                 }
1688             String attrName;
1689             p2 = getWord(p, attrName);
1690             if (p2==p)
1691                 break;
1692             //printf("name:%s",buf);
1693             p=p2;
1694             p = skipwhite(p);
1695             ch = peek(p);
1696             //printf("ch:%c\n",ch);
1697             if (ch!='=')
1698                 break;
1699             p++;
1700             p = skipwhite(p);
1701             // ch = parsebuf[p];
1702             // printf("ch:%c\n",ch);
1703             String attrVal;
1704             p2 = getQuoted(p, attrVal, true);
1705             p=p2+1;
1706             //printf("name:'%s'   value:'%s'\n",attrName.c_str(),attrVal.c_str());
1707             char *namestr = (char *)attrName.c_str();
1708             if (strncmp(namestr, "xmlns:", 6)==0)
1709                 n->addNamespace(attrName, attrVal);
1710             else
1711                 n->addAttribute(attrName, attrVal);
1712             }
1713         }
1715     bool cdata = false;
1717     p++;
1718     // ### Get intervening data ### */
1719     String data;
1720     while (p<parselen)
1721         {
1722         //# COMMENT
1723         p2 = match(p, "<!--");
1724         if (!cdata && p2>p)
1725             {
1726             p = p2;
1727             while (p<parselen)
1728                 {
1729                 p2 = match(p, "-->");
1730                 if (p2 > p)
1731                     {
1732                     p = p2;
1733                     break;
1734                     }
1735                 p++;
1736                 }
1737             }
1739         ch = peek(p);
1740         //# END TAG
1741         if (ch=='<' && !cdata && peek(p+1)=='/')
1742             {
1743             break;
1744             }
1745         //# CDATA
1746         p2 = match(p, "<![CDATA[");
1747         if (p2 > p)
1748             {
1749             cdata = true;
1750             p = p2;
1751             continue;
1752             }
1754         //# CHILD ELEMENT
1755         if (ch == '<')
1756             {
1757             p2 = parseElement(p, n, lineNr + countLines(p0, p));
1758             if (p2 == p)
1759                 {
1760                 /*
1761                 printf("problem on element:%s.  p2:%d p:%d\n",
1762                       openTagName.c_str(), p2, p);
1763                 */
1764                 return p0;
1765                 }
1766             p = p2;
1767             continue;
1768             }
1769         //# ENTITY
1770         if (ch=='&' && !cdata)
1771             {
1772             bool found = false;
1773             for (EntityEntry *ee = entities ; ee->value ; ee++)
1774                 {
1775                 int p2 = match(p, ee->escaped);
1776                 if (p2>p)
1777                     {
1778                     data.push_back(ee->value);
1779                     p = p2;
1780                     found = true;
1781                     break;
1782                     }
1783                 }
1784             if (!found)
1785                 {
1786                 error("unterminated entity");
1787                 return -1;
1788                 }
1789             continue;
1790             }
1792         //# NONE OF THE ABOVE
1793         data.push_back(ch);
1794         p++;
1795         }/*while*/
1798     n->value = data;
1799     //printf("%d : data:%s\n",p,data.c_str());
1801     //## Get close tag
1802     p = skipwhite(p);
1803     ch = peek(p);
1804     if (ch != '<')
1805         {
1806         error("no < for end tag\n");
1807         return p0;
1808         }
1809     p++;
1810     ch = peek(p);
1811     if (ch != '/')
1812         {
1813         error("no / on end tag");
1814         return p0;
1815         }
1816     p++;
1817     ch = peek(p);
1818     p = skipwhite(p);
1819     String closeTagName;
1820     p = getWord(p, closeTagName);
1821     if (openTagName != closeTagName)
1822         {
1823         error("Mismatched closing tag.  Expected </%S>. Got '%S'.",
1824                 openTagName.c_str(), closeTagName.c_str());
1825         return p0;
1826         }
1827     p = skipwhite(p);
1828     if (peek(p) != '>')
1829         {
1830         error("no > on end tag for '%s'", closeTagName.c_str());
1831         return p0;
1832         }
1833     p++;
1834     // printf("close element:%s\n",closeTagName.c_str());
1835     p = skipwhite(p);
1836     return p;
1842 Element *Parser::parse(XMLCh *buf,int pos,int len)
1844     parselen = len;
1845     parsebuf = buf;
1846     Element *rootNode = new Element("root");
1847     pos = parseVersion(pos);
1848     pos = parseDoctype(pos);
1849     pos = parseElement(pos, rootNode, 1);
1850     return rootNode;
1854 Element *Parser::parse(const char *buf, int pos, int len)
1856     XMLCh *charbuf = new XMLCh[len + 1];
1857     long i = 0;
1858     for ( ; i < len ; i++)
1859         charbuf[i] = (XMLCh)buf[i];
1860     charbuf[i] = '\0';
1862     Element *n = parse(charbuf, pos, len);
1863     delete[] charbuf;
1864     return n;
1867 Element *Parser::parse(const String &buf)
1869     long len = (long)buf.size();
1870     XMLCh *charbuf = new XMLCh[len + 1];
1871     long i = 0;
1872     for ( ; i < len ; i++)
1873         charbuf[i] = (XMLCh)buf[i];
1874     charbuf[i] = '\0';
1876     Element *n = parse(charbuf, 0, len);
1877     delete[] charbuf;
1878     return n;
1881 Element *Parser::parseFile(const String &fileName)
1884     //##### LOAD INTO A CHAR BUF, THEN CONVERT TO XMLCh
1885     FILE *f = fopen(fileName.c_str(), "rb");
1886     if (!f)
1887         return NULL;
1889     struct stat  statBuf;
1890     if (fstat(fileno(f),&statBuf)<0)
1891         {
1892         fclose(f);
1893         return NULL;
1894         }
1895     long filelen = statBuf.st_size;
1897     //printf("length:%d\n",filelen);
1898     XMLCh *charbuf = new XMLCh[filelen + 1];
1899     for (XMLCh *p=charbuf ; !feof(f) ; p++)
1900         {
1901         *p = (XMLCh)fgetc(f);
1902         }
1903     fclose(f);
1904     charbuf[filelen] = '\0';
1907     /*
1908     printf("nrbytes:%d\n",wc_count);
1909     printf("buf:%ls\n======\n",charbuf);
1910     */
1911     Element *n = parse(charbuf, 0, filelen);
1912     delete[] charbuf;
1913     return n;
1916 //########################################################################
1917 //########################################################################
1918 //##  E N D    X M L
1919 //########################################################################
1920 //########################################################################
1927 //########################################################################
1928 //########################################################################
1929 //##  U R I
1930 //########################################################################
1931 //########################################################################
1933 //This would normally be a call to a UNICODE function
1934 #define isLetter(x) isalpha(x)
1936 /**
1937  *  A class that implements the W3C URI resource reference.
1938  */
1939 class URI
1941 public:
1943     typedef enum
1944         {
1945         SCHEME_NONE =0,
1946         SCHEME_DATA,
1947         SCHEME_HTTP,
1948         SCHEME_HTTPS,
1949         SCHEME_FTP,
1950         SCHEME_FILE,
1951         SCHEME_LDAP,
1952         SCHEME_MAILTO,
1953         SCHEME_NEWS,
1954         SCHEME_TELNET
1955         } SchemeTypes;
1957     /**
1958      *
1959      */
1960     URI()
1961         {
1962         init();
1963         }
1965     /**
1966      *
1967      */
1968     URI(const String &str)
1969         {
1970         init();
1971         parse(str);
1972         }
1975     /**
1976      *
1977      */
1978     URI(const char *str)
1979         {
1980         init();
1981         String domStr = str;
1982         parse(domStr);
1983         }
1986     /**
1987      *
1988      */
1989     URI(const URI &other)
1990         {
1991         init();
1992         assign(other);
1993         }
1996     /**
1997      *
1998      */
1999     URI &operator=(const URI &other)
2000         {
2001         init();
2002         assign(other);
2003         return *this;
2004         }
2007     /**
2008      *
2009      */
2010     virtual ~URI()
2011         {}
2015     /**
2016      *
2017      */
2018     virtual bool parse(const String &str);
2020     /**
2021      *
2022      */
2023     virtual String toString() const;
2025     /**
2026      *
2027      */
2028     virtual int getScheme() const;
2030     /**
2031      *
2032      */
2033     virtual String getSchemeStr() const;
2035     /**
2036      *
2037      */
2038     virtual String getAuthority() const;
2040     /**
2041      *  Same as getAuthority, but if the port has been specified
2042      *  as host:port , the port will not be included
2043      */
2044     virtual String getHost() const;
2046     /**
2047      *
2048      */
2049     virtual int getPort() const;
2051     /**
2052      *
2053      */
2054     virtual String getPath() const;
2056     /**
2057      *
2058      */
2059     virtual String getNativePath() const;
2061     /**
2062      *
2063      */
2064     virtual bool isAbsolute() const;
2066     /**
2067      *
2068      */
2069     virtual bool isOpaque() const;
2071     /**
2072      *
2073      */
2074     virtual String getQuery() const;
2076     /**
2077      *
2078      */
2079     virtual String getFragment() const;
2081     /**
2082      *
2083      */
2084     virtual URI resolve(const URI &other) const;
2086     /**
2087      *
2088      */
2089     virtual void normalize();
2091 private:
2093     /**
2094      *
2095      */
2096     void init()
2097         {
2098         parsebuf  = NULL;
2099         parselen  = 0;
2100         scheme    = SCHEME_NONE;
2101         schemeStr = "";
2102         port      = 0;
2103         authority = "";
2104         path      = "";
2105         absolute  = false;
2106         opaque    = false;
2107         query     = "";
2108         fragment  = "";
2109         }
2112     /**
2113      *
2114      */
2115     void assign(const URI &other)
2116         {
2117         scheme    = other.scheme;
2118         schemeStr = other.schemeStr;
2119         authority = other.authority;
2120         port      = other.port;
2121         path      = other.path;
2122         absolute  = other.absolute;
2123         opaque    = other.opaque;
2124         query     = other.query;
2125         fragment  = other.fragment;
2126         }
2128     int scheme;
2130     String schemeStr;
2132     String authority;
2134     bool portSpecified;
2136     int port;
2138     String path;
2140     bool absolute;
2142     bool opaque;
2144     String query;
2146     String fragment;
2148     void error(const char *fmt, ...);
2150     void trace(const char *fmt, ...);
2153     int peek(int p);
2155     int match(int p, const char *key);
2157     int parseScheme(int p);
2159     int parseHierarchicalPart(int p0);
2161     int parseQuery(int p0);
2163     int parseFragment(int p0);
2165     int parse(int p);
2167     char *parsebuf;
2169     int parselen;
2171 };
2175 typedef struct
2177     int         ival;
2178     const char *sval;
2179     int         port;
2180 } LookupEntry;
2182 LookupEntry schemes[] =
2184     { URI::SCHEME_DATA,   "data:",    0 },
2185     { URI::SCHEME_HTTP,   "http:",   80 },
2186     { URI::SCHEME_HTTPS,  "https:", 443 },
2187     { URI::SCHEME_FTP,    "ftp",     12 },
2188     { URI::SCHEME_FILE,   "file:",    0 },
2189     { URI::SCHEME_LDAP,   "ldap:",  123 },
2190     { URI::SCHEME_MAILTO, "mailto:", 25 },
2191     { URI::SCHEME_NEWS,   "news:",  117 },
2192     { URI::SCHEME_TELNET, "telnet:", 23 },
2193     { 0,                  NULL,       0 }
2194 };
2197 String URI::toString() const
2199     String str = schemeStr;
2200     if (authority.size() > 0)
2201         {
2202         str.append("//");
2203         str.append(authority);
2204         }
2205     str.append(path);
2206     if (query.size() > 0)
2207         {
2208         str.append("?");
2209         str.append(query);
2210         }
2211     if (fragment.size() > 0)
2212         {
2213         str.append("#");
2214         str.append(fragment);
2215         }
2216     return str;
2220 int URI::getScheme() const
2222     return scheme;
2225 String URI::getSchemeStr() const
2227     return schemeStr;
2231 String URI::getAuthority() const
2233     String ret = authority;
2234     if (portSpecified && port>=0)
2235         {
2236         char buf[7];
2237         snprintf(buf, 6, ":%6d", port);
2238         ret.append(buf);
2239         }
2240     return ret;
2243 String URI::getHost() const
2245     return authority;
2248 int URI::getPort() const
2250     return port;
2254 String URI::getPath() const
2256     return path;
2259 String URI::getNativePath() const
2261     String npath;
2262 #ifdef __WIN32__
2263     unsigned int firstChar = 0;
2264     if (path.size() >= 3)
2265         {
2266         if (path[0] == '/' &&
2267             isLetter(path[1]) &&
2268             path[2] == ':')
2269             firstChar++;
2270          }
2271     for (unsigned int i=firstChar ; i<path.size() ; i++)
2272         {
2273         XMLCh ch = (XMLCh) path[i];
2274         if (ch == '/')
2275             npath.push_back((XMLCh)'\\');
2276         else
2277             npath.push_back(ch);
2278         }
2279 #else
2280     npath = path;
2281 #endif
2282     return npath;
2286 bool URI::isAbsolute() const
2288     return absolute;
2291 bool URI::isOpaque() const
2293     return opaque;
2297 String URI::getQuery() const
2299     return query;
2303 String URI::getFragment() const
2305     return fragment;
2309 URI URI::resolve(const URI &other) const
2311     //### According to w3c, this is handled in 3 cases
2313     //## 1
2314     if (opaque || other.isAbsolute())
2315         return other;
2317     //## 2
2318     if (other.fragment.size()  >  0 &&
2319         other.path.size()      == 0 &&
2320         other.scheme           == SCHEME_NONE &&
2321         other.authority.size() == 0 &&
2322         other.query.size()     == 0 )
2323         {
2324         URI fragUri = *this;
2325         fragUri.fragment = other.fragment;
2326         return fragUri;
2327         }
2329     //## 3 http://www.ietf.org/rfc/rfc2396.txt, section 5.2
2330     URI newUri;
2331     //# 3.1
2332     newUri.scheme    = scheme;
2333     newUri.schemeStr = schemeStr;
2334     newUri.query     = other.query;
2335     newUri.fragment  = other.fragment;
2336     if (other.authority.size() > 0)
2337         {
2338         //# 3.2
2339         if (absolute || other.absolute)
2340             newUri.absolute = true;
2341         newUri.authority = other.authority;
2342         newUri.port      = other.port;//part of authority
2343         newUri.path      = other.path;
2344         }
2345     else
2346         {
2347         //# 3.3
2348         if (other.absolute)
2349             {
2350             newUri.absolute = true;
2351             newUri.path     = other.path;
2352             }
2353         else
2354             {
2355             unsigned int pos = path.find_last_of('/');
2356             if (pos != path.npos)
2357                 {
2358                 String tpath = path.substr(0, pos+1);
2359                 tpath.append(other.path);
2360                 newUri.path = tpath;
2361                 }
2362             else
2363                 newUri.path = other.path;
2364             }
2365         }
2367     newUri.normalize();
2368     return newUri;
2373 /**
2374  *  This follows the Java URI algorithm:
2375  *   1. All "." segments are removed.
2376  *   2. If a ".." segment is preceded by a non-".." segment
2377  *          then both of these segments are removed. This step
2378  *          is repeated until it is no longer applicable.
2379  *   3. If the path is relative, and if its first segment
2380  *          contains a colon character (':'), then a "." segment
2381  *          is prepended. This prevents a relative URI with a path
2382  *          such as "a:b/c/d" from later being re-parsed as an
2383  *          opaque URI with a scheme of "a" and a scheme-specific
2384  *          part of "b/c/d". (Deviation from RFC 2396)
2385  */
2386 void URI::normalize()
2388     std::vector<String> segments;
2390     //## Collect segments
2391     if (path.size()<2)
2392         return;
2393     bool abs = false;
2394     unsigned int pos=0;
2395     if (path[0]=='/')
2396         {
2397         abs = true;
2398         pos++;
2399         }
2400     while (pos < path.size())
2401         {
2402         unsigned int pos2 = path.find('/', pos);
2403         if (pos2==path.npos)
2404             {
2405             String seg = path.substr(pos);
2406             //printf("last segment:%s\n", seg.c_str());
2407             segments.push_back(seg);
2408             break;
2409             }
2410         if (pos2>pos)
2411             {
2412             String seg = path.substr(pos, pos2-pos);
2413             //printf("segment:%s\n", seg.c_str());
2414             segments.push_back(seg);
2415             }
2416         pos = pos2;
2417         pos++;
2418         }
2420     //## Clean up (normalize) segments
2421     bool edited = false;
2422     std::vector<String>::iterator iter;
2423     for (iter=segments.begin() ; iter!=segments.end() ; )
2424         {
2425         String s = *iter;
2426         if (s == ".")
2427             {
2428             iter = segments.erase(iter);
2429             edited = true;
2430             }
2431         else if (s == ".." &&
2432                  iter != segments.begin() &&
2433                  *(iter-1) != "..")
2434             {
2435             iter--; //back up, then erase two entries
2436             iter = segments.erase(iter);
2437             iter = segments.erase(iter);
2438             edited = true;
2439             }
2440         else
2441             iter++;
2442         }
2444     //## Rebuild path, if necessary
2445     if (edited)
2446         {
2447         path.clear();
2448         if (abs)
2449             {
2450             path.append("/");
2451             }
2452         std::vector<String>::iterator iter;
2453         for (iter=segments.begin() ; iter!=segments.end() ; iter++)
2454             {
2455             if (iter != segments.begin())
2456                 path.append("/");
2457             path.append(*iter);
2458             }
2459         }
2465 //#########################################################################
2466 //# M E S S A G E S
2467 //#########################################################################
2469 void URI::error(const char *fmt, ...)
2471     va_list args;
2472     fprintf(stderr, "URI error: ");
2473     va_start(args, fmt);
2474     vfprintf(stderr, fmt, args);
2475     va_end(args);
2476     fprintf(stderr, "\n");
2479 void URI::trace(const char *fmt, ...)
2481     va_list args;
2482     fprintf(stdout, "URI: ");
2483     va_start(args, fmt);
2484     vfprintf(stdout, fmt, args);
2485     va_end(args);
2486     fprintf(stdout, "\n");
2492 //#########################################################################
2493 //# P A R S I N G
2494 //#########################################################################
2498 int URI::peek(int p)
2500     if (p<0 || p>=parselen)
2501         return -1;
2502     return parsebuf[p];
2507 int URI::match(int p0, const char *key)
2509     int p = p0;
2510     while (p < parselen)
2511         {
2512         if (*key == '\0')
2513             return p;
2514         else if (*key != parsebuf[p])
2515             break;
2516         p++; key++;
2517         }
2518     return p0;
2521 //#########################################################################
2522 //#  Parsing is performed according to:
2523 //#  http://www.gbiv.com/protocols/uri/rfc/rfc3986.html#components
2524 //#########################################################################
2526 int URI::parseScheme(int p0)
2528     int p = p0;
2529     for (LookupEntry *entry = schemes; entry->sval ; entry++)
2530         {
2531         int p2 = match(p, entry->sval);
2532         if (p2 > p)
2533             {
2534             schemeStr = entry->sval;
2535             scheme    = entry->ival;
2536             port      = entry->port;
2537             p = p2;
2538             return p;
2539             }
2540         }
2542     return p;
2546 int URI::parseHierarchicalPart(int p0)
2548     int p = p0;
2549     int ch;
2551     //# Authority field (host and port, for example)
2552     int p2 = match(p, "//");
2553     if (p2 > p)
2554         {
2555         p = p2;
2556         portSpecified = false;
2557         String portStr;
2558         while (p < parselen)
2559             {
2560             ch = peek(p);
2561             if (ch == '/')
2562                 break;
2563             else if (ch == ':')
2564                 portSpecified = true;
2565             else if (portSpecified)
2566                 portStr.push_back((XMLCh)ch);
2567             else
2568                 authority.push_back((XMLCh)ch);
2569             p++;
2570             }
2571         if (portStr.size() > 0)
2572             {
2573             char *pstr = (char *)portStr.c_str();
2574             char *endStr;
2575             long val = strtol(pstr, &endStr, 10);
2576             if (endStr > pstr) //successful parse?
2577                 port = val;
2578             }
2579         }
2581     //# Are we absolute?
2582     ch = peek(p);
2583     if (isLetter(ch) && peek(p+1)==':')
2584         {
2585         absolute = true;
2586         path.push_back((XMLCh)'/');
2587         }
2588     else if (ch == '/')
2589         {
2590         absolute = true;
2591         if (p>p0) //in other words, if '/' is not the first char
2592             opaque = true;
2593         path.push_back((XMLCh)ch);
2594         p++;
2595         }
2597     while (p < parselen)
2598         {
2599         ch = peek(p);
2600         if (ch == '?' || ch == '#')
2601             break;
2602         path.push_back((XMLCh)ch);
2603         p++;
2604         }
2606     return p;
2609 int URI::parseQuery(int p0)
2611     int p = p0;
2612     int ch = peek(p);
2613     if (ch != '?')
2614         return p0;
2616     p++;
2617     while (p < parselen)
2618         {
2619         ch = peek(p);
2620         if (ch == '#')
2621             break;
2622         query.push_back((XMLCh)ch);
2623         p++;
2624         }
2627     return p;
2630 int URI::parseFragment(int p0)
2633     int p = p0;
2634     int ch = peek(p);
2635     if (ch != '#')
2636         return p0;
2638     p++;
2639     while (p < parselen)
2640         {
2641         ch = peek(p);
2642         if (ch == '?')
2643             break;
2644         fragment.push_back((XMLCh)ch);
2645         p++;
2646         }
2649     return p;
2653 int URI::parse(int p0)
2656     int p = p0;
2658     int p2 = parseScheme(p);
2659     if (p2 < 0)
2660         {
2661         error("Scheme");
2662         return -1;
2663         }
2664     p = p2;
2667     p2 = parseHierarchicalPart(p);
2668     if (p2 < 0)
2669         {
2670         error("Hierarchical part");
2671         return -1;
2672         }
2673     p = p2;
2675     p2 = parseQuery(p);
2676     if (p2 < 0)
2677         {
2678         error("Query");
2679         return -1;
2680         }
2681     p = p2;
2684     p2 = parseFragment(p);
2685     if (p2 < 0)
2686         {
2687         error("Fragment");
2688         return -1;
2689         }
2690     p = p2;
2692     return p;
2698 bool URI::parse(const String &str)
2700     init();
2701     
2702     parselen = str.size();
2704     String tmp;
2705     for (unsigned int i=0 ; i<str.size() ; i++)
2706         {
2707         XMLCh ch = (XMLCh) str[i];
2708         if (ch == '\\')
2709             tmp.push_back((XMLCh)'/');
2710         else
2711             tmp.push_back(ch);
2712         }
2713     parsebuf = (char *) tmp.c_str();
2716     int p = parse(0);
2717     normalize();
2719     if (p < 0)
2720         {
2721         error("Syntax error");
2722         return false;
2723         }
2725     //printf("uri:%s\n", toString().c_str());
2726     //printf("path:%s\n", path.c_str());
2728     return true;
2739 //########################################################################
2740 //########################################################################
2741 //##  M A K E
2742 //########################################################################
2743 //########################################################################
2745 //########################################################################
2746 //# Stat cache to speed up stat requests
2747 //########################################################################
2748 typedef std::map<String, std::pair<int,struct stat> > statCacheType;
2749 static statCacheType statCache;
2750 static int cachedStat(const String &f, struct stat *s) {
2751     std::pair<statCacheType::iterator, bool> result = statCache.insert(statCacheType::value_type(f, std::pair<int,struct stat>()));
2752     if (result.second) {
2753         result.first->second.first = stat(f.c_str(), &(result.first->second.second));
2754     }
2755     *s = result.first->second.second;
2756     return result.first->second.first;
2759 //########################################################################
2760 //# F I L E S E T
2761 //########################################################################
2762 /**
2763  * This is the descriptor for a <fileset> item
2764  */
2765 class FileSet
2767 public:
2769     /**
2770      *
2771      */
2772     FileSet()
2773         {}
2775     /**
2776      *
2777      */
2778     FileSet(const FileSet &other)
2779         { assign(other); }
2781     /**
2782      *
2783      */
2784     FileSet &operator=(const FileSet &other)
2785         { assign(other); return *this; }
2787     /**
2788      *
2789      */
2790     virtual ~FileSet()
2791         {}
2793     /**
2794      *
2795      */
2796     String getDirectory() const
2797         { return directory; }
2798         
2799     /**
2800      *
2801      */
2802     void setDirectory(const String &val)
2803         { directory = val; }
2805     /**
2806      *
2807      */
2808     void setFiles(const std::vector<String> &val)
2809         { files = val; }
2811     /**
2812      *
2813      */
2814     std::vector<String> getFiles() const
2815         { return files; }
2816         
2817     /**
2818      *
2819      */
2820     void setIncludes(const std::vector<String> &val)
2821         { includes = val; }
2823     /**
2824      *
2825      */
2826     std::vector<String> getIncludes() const
2827         { return includes; }
2828         
2829     /**
2830      *
2831      */
2832     void setExcludes(const std::vector<String> &val)
2833         { excludes = val; }
2835     /**
2836      *
2837      */
2838     std::vector<String> getExcludes() const
2839         { return excludes; }
2840         
2841     /**
2842      *
2843      */
2844     unsigned int size() const
2845         { return files.size(); }
2846         
2847     /**
2848      *
2849      */
2850     String operator[](int index) const
2851         { return files[index]; }
2852         
2853     /**
2854      *
2855      */
2856     void clear()
2857         {
2858         directory = "";
2859         files.clear();
2860         includes.clear();
2861         excludes.clear();
2862         }
2863         
2865 private:
2867     void assign(const FileSet &other)
2868         {
2869         directory = other.directory;
2870         files     = other.files;
2871         includes  = other.includes;
2872         excludes  = other.excludes;
2873         }
2875     String directory;
2876     std::vector<String> files;
2877     std::vector<String> includes;
2878     std::vector<String> excludes;
2879 };
2882 //########################################################################
2883 //# F I L E L I S T
2884 //########################################################################
2885 /**
2886  * This is a simpler, explicitly-named list of files
2887  */
2888 class FileList
2890 public:
2892     /**
2893      *
2894      */
2895     FileList()
2896         {}
2898     /**
2899      *
2900      */
2901     FileList(const FileList &other)
2902         { assign(other); }
2904     /**
2905      *
2906      */
2907     FileList &operator=(const FileList &other)
2908         { assign(other); return *this; }
2910     /**
2911      *
2912      */
2913     virtual ~FileList()
2914         {}
2916     /**
2917      *
2918      */
2919     String getDirectory()
2920         { return directory; }
2921         
2922     /**
2923      *
2924      */
2925     void setDirectory(const String &val)
2926         { directory = val; }
2928     /**
2929      *
2930      */
2931     void setFiles(const std::vector<String> &val)
2932         { files = val; }
2934     /**
2935      *
2936      */
2937     std::vector<String> getFiles()
2938         { return files; }
2939         
2940     /**
2941      *
2942      */
2943     unsigned int size()
2944         { return files.size(); }
2945         
2946     /**
2947      *
2948      */
2949     String operator[](int index)
2950         { return files[index]; }
2951         
2952     /**
2953      *
2954      */
2955     void clear()
2956         {
2957         directory = "";
2958         files.clear();
2959         }
2960         
2962 private:
2964     void assign(const FileList &other)
2965         {
2966         directory = other.directory;
2967         files     = other.files;
2968         }
2970     String directory;
2971     std::vector<String> files;
2972 };
2977 //########################################################################
2978 //# M A K E    B A S E
2979 //########################################################################
2980 /**
2981  * Base class for all classes in this file
2982  */
2983 class MakeBase
2985 public:
2987     MakeBase()
2988         { line = 0; }
2989     virtual ~MakeBase()
2990         {}
2992     /**
2993      *     Return the URI of the file associated with this object 
2994      */     
2995     URI getURI()
2996         { return uri; }
2998     /**
2999      * Set the uri to the given string
3000      */
3001     void setURI(const String &uristr)
3002         { uri.parse(uristr); }
3004     /**
3005      *  Resolve another path relative to this one
3006      */
3007     String resolve(const String &otherPath);
3009     /**
3010      * replace variable refs like ${a} with their values
3011      * Assume that the string has already been syntax validated
3012      */
3013     String eval(const String &s, const String &defaultVal);
3015     /**
3016      * replace variable refs like ${a} with their values
3017      * return true or false
3018      * Assume that the string has already been syntax validated
3019      */
3020     bool evalBool(const String &s, bool defaultVal);
3022     /**
3023      *  Get an element attribute, performing substitutions if necessary
3024      */
3025     bool getAttribute(Element *elem, const String &name, String &result);
3027     /**
3028      * Get an element value, performing substitutions if necessary
3029      */
3030     bool getValue(Element *elem, String &result);
3031     
3032     /**
3033      * Set the current line number in the file
3034      */         
3035     void setLine(int val)
3036         { line = val; }
3037         
3038     /**
3039      * Get the current line number in the file
3040      */         
3041     int getLine()
3042         { return line; }
3045     /**
3046      * Set a property to a given value
3047      */
3048     virtual void setProperty(const String &name, const String &val)
3049         {
3050         properties[name] = val;
3051         }
3053     /**
3054      * Return a named property is found, else a null string
3055      */
3056     virtual String getProperty(const String &name)
3057         {
3058         String val;
3059         std::map<String, String>::iterator iter = properties.find(name);
3060         if (iter != properties.end())
3061             val = iter->second;
3062         String sval;
3063         if (!getSubstitutions(val, sval))
3064             return false;
3065         return sval;
3066         }
3068     /**
3069      * Return true if a named property is found, else false
3070      */
3071     virtual bool hasProperty(const String &name)
3072         {
3073         std::map<String, String>::iterator iter = properties.find(name);
3074         if (iter == properties.end())
3075             return false;
3076         return true;
3077         }
3080 protected:
3082     /**
3083      *    The path to the file associated with this object
3084      */     
3085     URI uri;
3086     
3087     /**
3088      *    If this prefix is seen in a substitution, use an environment
3089      *    variable.
3090      *             example:  <property environment="env"/>
3091      *             ${env.JAVA_HOME}
3092      */
3093     String envPrefix;
3095     /**
3096      *    If this prefix is seen in a substitution, use as a
3097      *    pkg-config 'all' query
3098      *             example:  <property pkg-config="pc"/>
3099      *             ${pc.gtkmm}
3100      */
3101     String pcPrefix;
3103     /**
3104      *    If this prefix is seen in a substitution, use as a
3105      *    pkg-config 'cflags' query
3106      *             example:  <property pkg-config="pcc"/>
3107      *             ${pcc.gtkmm}
3108      */
3109     String pccPrefix;
3111     /**
3112      *    If this prefix is seen in a substitution, use as a
3113      *    pkg-config 'libs' query
3114      *             example:  <property pkg-config="pcl"/>
3115      *             ${pcl.gtkmm}
3116      */
3117     String pclPrefix;
3123     /**
3124      *  Print a printf()-like formatted error message
3125      */
3126     void error(const char *fmt, ...);
3128     /**
3129      *  Print a printf()-like formatted trace message
3130      */
3131     void status(const char *fmt, ...);
3133     /**
3134      *  Show target status
3135      */
3136     void targetstatus(const char *fmt, ...);
3138     /**
3139      *  Print a printf()-like formatted trace message
3140      */
3141     void trace(const char *fmt, ...);
3143     /**
3144      *  Check if a given string matches a given regex pattern
3145      */
3146     bool regexMatch(const String &str, const String &pattern);
3148     /**
3149      *
3150      */
3151     String getSuffix(const String &fname);
3153     /**
3154      * Break up a string into substrings delimited the characters
3155      * in delimiters.  Null-length substrings are ignored
3156      */  
3157     std::vector<String> tokenize(const String &val,
3158                           const String &delimiters);
3160     /**
3161      *  replace runs of whitespace with a space
3162      */
3163     String strip(const String &s);
3165     /**
3166      *  remove leading whitespace from each line
3167      */
3168     String leftJustify(const String &s);
3170     /**
3171      *  remove leading and trailing whitespace from string
3172      */
3173     String trim(const String &s);
3175     /**
3176      *  Return a lower case version of the given string
3177      */
3178     String toLower(const String &s);
3180     /**
3181      * Return the native format of the canonical
3182      * path which we store
3183      */
3184     String getNativePath(const String &path);
3186     /**
3187      * Execute a shell command.  Outbuf is a ref to a string
3188      * to catch the result.     
3189      */         
3190     bool executeCommand(const String &call,
3191                         const String &inbuf,
3192                         String &outbuf,
3193                         String &errbuf);
3194     /**
3195      * List all directories in a given base and starting directory
3196      * It is usually called like:
3197      *        bool ret = listDirectories("src", "", result);    
3198      */         
3199     bool listDirectories(const String &baseName,
3200                          const String &dirname,
3201                          std::vector<String> &res);
3203     /**
3204      * Find all files in the named directory 
3205      */         
3206     bool listFiles(const String &baseName,
3207                    const String &dirname,
3208                    std::vector<String> &result);
3210     /**
3211      * Perform a listing for a fileset 
3212      */         
3213     bool listFiles(MakeBase &propRef, FileSet &fileSet);
3215     /**
3216      * Parse a <patternset>
3217      */  
3218     bool parsePatternSet(Element *elem,
3219                        MakeBase &propRef,
3220                        std::vector<String> &includes,
3221                        std::vector<String> &excludes);
3223     /**
3224      * Parse a <fileset> entry, and determine which files
3225      * should be included
3226      */  
3227     bool parseFileSet(Element *elem,
3228                     MakeBase &propRef,
3229                     FileSet &fileSet);
3230     /**
3231      * Parse a <filelist> entry
3232      */  
3233     bool parseFileList(Element *elem,
3234                     MakeBase &propRef,
3235                     FileList &fileList);
3237     /**
3238      * Return this object's property list
3239      */
3240     virtual std::map<String, String> &getProperties()
3241         { return properties; }
3244     std::map<String, String> properties;
3246     /**
3247      * Create a directory, making intermediate dirs
3248      * if necessary
3249      */                  
3250     bool createDirectory(const String &dirname);
3252     /**
3253      * Delete a directory and its children if desired
3254      */
3255     bool removeDirectory(const String &dirName);
3257     /**
3258      * Copy a file from one name to another. Perform only if needed
3259      */ 
3260     bool copyFile(const String &srcFile, const String &destFile);
3262     /**
3263      * Tests if the file exists and is a regular file
3264      */ 
3265     bool isRegularFile(const String &fileName);
3267     /**
3268      * Tests if the file exists and is a directory
3269      */ 
3270     bool isDirectory(const String &fileName);
3272     /**
3273      * Tests is the modification date of fileA is newer than fileB
3274      */ 
3275     bool isNewerThan(const String &fileA, const String &fileB);
3277 private:
3279     bool pkgConfigRecursive(const String packageName,
3280                             const String &path, 
3281                             const String &prefix, 
3282                             int query,
3283                             String &result,
3284                             std::set<String> &deplist);
3286     /**
3287      * utility method to query for "all", "cflags", or "libs" for this package and its
3288      * dependencies.  0, 1, 2
3289      */          
3290     bool pkgConfigQuery(const String &packageName, int query, String &result);
3292     /**
3293      * replace a variable ref like ${a} with a value
3294      */
3295     bool lookupProperty(const String &s, String &result);
3296     
3297     /**
3298      * called by getSubstitutions().  This is in case a looked-up string
3299      * has substitutions also.     
3300      */
3301     bool getSubstitutionsRecursive(const String &s, String &result, int depth);
3303     /**
3304      * replace variable refs in a string like ${a} with their values
3305      */
3306     bool getSubstitutions(const String &s, String &result);
3308     int line;
3311 };
3315 /**
3316  * Define the pkg-config class here, since it will be used in MakeBase method
3317  * implementations. 
3318  */
3319 class PkgConfig : public MakeBase
3322 public:
3324     /**
3325      *
3326      */
3327     PkgConfig()
3328         {
3329          path   = ".";
3330          prefix = "/target";
3331          init();
3332          }
3334     /**
3335      *
3336      */
3337     PkgConfig(const PkgConfig &other)
3338         { assign(other); }
3340     /**
3341      *
3342      */
3343     PkgConfig &operator=(const PkgConfig &other)
3344         { assign(other); return *this; }
3346     /**
3347      *
3348      */
3349     virtual ~PkgConfig()
3350         { }
3352     /**
3353      *
3354      */
3355     virtual String getName()
3356         { return name; }
3358     /**
3359      *
3360      */
3361     virtual String getPath()
3362         { return path; }
3364     /**
3365      *
3366      */
3367     virtual void setPath(const String &val)
3368         { path = val; }
3370     /**
3371      *
3372      */
3373     virtual String getPrefix()
3374         { return prefix; }
3376     /**
3377      *  Allow the user to override the prefix in the file
3378      */
3379     virtual void setPrefix(const String &val)
3380         { prefix = val; }
3382     /**
3383      *
3384      */
3385     virtual String getDescription()
3386         { return description; }
3388     /**
3389      *
3390      */
3391     virtual String getCflags()
3392         { return cflags; }
3394     /**
3395      *
3396      */
3397     virtual String getLibs()
3398         { return libs; }
3400     /**
3401      *
3402      */
3403     virtual String getAll()
3404         {
3405          String ret = cflags;
3406          ret.append(" ");
3407          ret.append(libs);
3408          return ret;
3409         }
3411     /**
3412      *
3413      */
3414     virtual String getVersion()
3415         { return version; }
3417     /**
3418      *
3419      */
3420     virtual int getMajorVersion()
3421         { return majorVersion; }
3423     /**
3424      *
3425      */
3426     virtual int getMinorVersion()
3427         { return minorVersion; }
3429     /**
3430      *
3431      */
3432     virtual int getMicroVersion()
3433         { return microVersion; }
3435     /**
3436      *
3437      */
3438     virtual std::map<String, String> &getAttributes()
3439         { return attrs; }
3441     /**
3442      *
3443      */
3444     virtual std::vector<String> &getRequireList()
3445         { return requireList; }
3447     /**
3448      *  Read a file for its details
3449      */         
3450     virtual bool readFile(const String &fileName);
3452     /**
3453      *  Read a file for its details
3454      */         
3455     virtual bool query(const String &name);
3457 private:
3459     void init()
3460         {
3461         //do not set path and prefix here
3462         name         = "";
3463         description  = "";
3464         cflags       = "";
3465         libs         = "";
3466         requires     = "";
3467         version      = "";
3468         majorVersion = 0;
3469         minorVersion = 0;
3470         microVersion = 0;
3471         fileName     = "";
3472         attrs.clear();
3473         requireList.clear();
3474         }
3476     void assign(const PkgConfig &other)
3477         {
3478         name         = other.name;
3479         path         = other.path;
3480         prefix       = other.prefix;
3481         description  = other.description;
3482         cflags       = other.cflags;
3483         libs         = other.libs;
3484         requires     = other.requires;
3485         version      = other.version;
3486         majorVersion = other.majorVersion;
3487         minorVersion = other.minorVersion;
3488         microVersion = other.microVersion;
3489         fileName     = other.fileName;
3490         attrs        = other.attrs;
3491         requireList  = other.requireList;
3492         }
3496     int get(int pos);
3498     int skipwhite(int pos);
3500     int getword(int pos, String &ret);
3502     /**
3503      * Very important
3504      */         
3505     bool parseRequires();
3507     void parseVersion();
3509     bool parseLine(const String &lineBuf);
3511     bool parse(const String &buf);
3513     void dumpAttrs();
3515     String name;
3517     String path;
3519     String prefix;
3521     String description;
3523     String cflags;
3525     String libs;
3527     String requires;
3529     String version;
3531     int majorVersion;
3533     int minorVersion;
3535     int microVersion;
3537     String fileName;
3539     std::map<String, String> attrs;
3541     std::vector<String> requireList;
3543     char *parsebuf;
3544     int parselen;
3545 };
3550 /**
3551  *  Print a printf()-like formatted error message
3552  */
3553 void MakeBase::error(const char *fmt, ...)
3555     va_list args;
3556     va_start(args,fmt);
3557     fprintf(stderr, "Make error line %d: ", line);
3558     vfprintf(stderr, fmt, args);
3559     fprintf(stderr, "\n");
3560     va_end(args) ;
3565 /**
3566  *  Print a printf()-like formatted trace message
3567  */
3568 void MakeBase::status(const char *fmt, ...)
3570     va_list args;
3571     //fprintf(stdout, " ");
3572     va_start(args,fmt);
3573     vfprintf(stdout, fmt, args);
3574     va_end(args);
3575     fprintf(stdout, "\n");
3576     fflush(stdout);
3580 /**
3581  *  Print a printf()-like formatted trace message
3582  */
3583 void MakeBase::trace(const char *fmt, ...)
3585     va_list args;
3586     fprintf(stdout, "Make: ");
3587     va_start(args,fmt);
3588     vfprintf(stdout, fmt, args);
3589     va_end(args) ;
3590     fprintf(stdout, "\n");
3591     fflush(stdout);
3596 /**
3597  *  Resolve another path relative to this one
3598  */
3599 String MakeBase::resolve(const String &otherPath)
3601     URI otherURI(otherPath);
3602     URI fullURI = uri.resolve(otherURI);
3603     String ret = fullURI.toString();
3604     return ret;
3609 /**
3610  *  Check if a given string matches a given regex pattern
3611  */
3612 bool MakeBase::regexMatch(const String &str, const String &pattern)
3614     const TRexChar *terror = NULL;
3615     const TRexChar *cpat = pattern.c_str();
3616     TRex *expr = trex_compile(cpat, &terror);
3617     if (!expr)
3618         {
3619         if (!terror)
3620             terror = "undefined";
3621         error("compilation error [%s]!\n", terror);
3622         return false;
3623         } 
3625     bool ret = true;
3627     const TRexChar *cstr = str.c_str();
3628     if (trex_match(expr, cstr))
3629         {
3630         ret = true;
3631         }
3632     else
3633         {
3634         ret = false;
3635         }
3637     trex_free(expr);
3639     return ret;
3642 /**
3643  *  Return the suffix, if any, of a file name
3644  */
3645 String MakeBase::getSuffix(const String &fname)
3647     if (fname.size() < 2)
3648         return "";
3649     unsigned int pos = fname.find_last_of('.');
3650     if (pos == fname.npos)
3651         return "";
3652     pos++;
3653     String res = fname.substr(pos, fname.size()-pos);
3654     //trace("suffix:%s", res.c_str()); 
3655     return res;
3660 /**
3661  * Break up a string into substrings delimited the characters
3662  * in delimiters.  Null-length substrings are ignored
3663  */  
3664 std::vector<String> MakeBase::tokenize(const String &str,
3665                                 const String &delimiters)
3668     std::vector<String> res;
3669     char *del = (char *)delimiters.c_str();
3670     String dmp;
3671     for (unsigned int i=0 ; i<str.size() ; i++)
3672         {
3673         char ch = str[i];
3674         char *p = (char *)0;
3675         for (p=del ; *p ; p++)
3676             if (*p == ch)
3677                 break;
3678         if (*p)
3679             {
3680             if (dmp.size() > 0)
3681                 {
3682                 res.push_back(dmp);
3683                 dmp.clear();
3684                 }
3685             }
3686         else
3687             {
3688             dmp.push_back(ch);
3689             }
3690         }
3691     //Add tail
3692     if (dmp.size() > 0)
3693         {
3694         res.push_back(dmp);
3695         dmp.clear();
3696         }
3698     return res;
3703 /**
3704  *  replace runs of whitespace with a single space
3705  */
3706 String MakeBase::strip(const String &s)
3708     int len = s.size();
3709     String stripped;
3710     for (int i = 0 ; i<len ; i++)
3711         {
3712         char ch = s[i];
3713         if (isspace(ch))
3714             {
3715             stripped.push_back(' ');
3716             for ( ; i<len ; i++)
3717                 {
3718                 ch = s[i];
3719                 if (!isspace(ch))
3720                     {
3721                     stripped.push_back(ch);
3722                     break;
3723                     }
3724                 }
3725             }
3726         else
3727             {
3728             stripped.push_back(ch);
3729             }
3730         }
3731     return stripped;
3734 /**
3735  *  remove leading whitespace from each line
3736  */
3737 String MakeBase::leftJustify(const String &s)
3739     String out;
3740     int len = s.size();
3741     for (int i = 0 ; i<len ; )
3742         {
3743         char ch;
3744         //Skip to first visible character
3745         while (i<len)
3746             {
3747             ch = s[i];
3748             if (ch == '\n' || ch == '\r'
3749               || !isspace(ch))
3750                   break;
3751             i++;
3752             }
3753         //Copy the rest of the line
3754         while (i<len)
3755             {
3756             ch = s[i];
3757             if (ch == '\n' || ch == '\r')
3758                 {
3759                 if (ch != '\r')
3760                     out.push_back('\n');
3761                 i++;
3762                 break;
3763                 }
3764             else
3765                 {
3766                 out.push_back(ch);
3767                 }
3768             i++;
3769             }
3770         }
3771     return out;
3775 /**
3776  *  Removes whitespace from beginning and end of a string
3777  */
3778 String MakeBase::trim(const String &s)
3780     if (s.size() < 1)
3781         return s;
3782     
3783     //Find first non-ws char
3784     unsigned int begin = 0;
3785     for ( ; begin < s.size() ; begin++)
3786         {
3787         if (!isspace(s[begin]))
3788             break;
3789         }
3791     //Find first non-ws char, going in reverse
3792     unsigned int end = s.size() - 1;
3793     for ( ; end > begin ; end--)
3794         {
3795         if (!isspace(s[end]))
3796             break;
3797         }
3798     //trace("begin:%d  end:%d", begin, end);
3800     String res = s.substr(begin, end-begin+1);
3801     return res;
3805 /**
3806  *  Return a lower case version of the given string
3807  */
3808 String MakeBase::toLower(const String &s)
3810     if (s.size()==0)
3811         return s;
3813     String ret;
3814     for(unsigned int i=0; i<s.size() ; i++)
3815         {
3816         ret.push_back(tolower(s[i]));
3817         }
3818     return ret;
3822 /**
3823  * Return the native format of the canonical
3824  * path which we store
3825  */
3826 String MakeBase::getNativePath(const String &path)
3828 #ifdef __WIN32__
3829     String npath;
3830     unsigned int firstChar = 0;
3831     if (path.size() >= 3)
3832         {
3833         if (path[0] == '/' &&
3834             isalpha(path[1]) &&
3835             path[2] == ':')
3836             firstChar++;
3837         }
3838     for (unsigned int i=firstChar ; i<path.size() ; i++)
3839         {
3840         char ch = path[i];
3841         if (ch == '/')
3842             npath.push_back('\\');
3843         else
3844             npath.push_back(ch);
3845         }
3846     return npath;
3847 #else
3848     return path;
3849 #endif
3853 #ifdef __WIN32__
3854 #include <tchar.h>
3856 static String win32LastError()
3859     DWORD dw = GetLastError(); 
3861     LPVOID str;
3862     FormatMessage(
3863         FORMAT_MESSAGE_ALLOCATE_BUFFER | 
3864         FORMAT_MESSAGE_FROM_SYSTEM,
3865         NULL,
3866         dw,
3867         0,
3868         (LPTSTR) &str,
3869         0, NULL );
3870     LPTSTR p = _tcschr((const char *)str, _T('\r'));
3871     if(p != NULL)
3872         { // lose CRLF
3873         *p = _T('\0');
3874         }
3875     String ret = (char *)str;
3876     LocalFree(str);
3878     return ret;
3880 #endif
3885 #ifdef __WIN32__
3887 /**
3888  * Execute a system call, using pipes to send data to the
3889  * program's stdin,  and reading stdout and stderr.
3890  */
3891 bool MakeBase::executeCommand(const String &command,
3892                               const String &inbuf,
3893                               String &outbuf,
3894                               String &errbuf)
3897     status("============ cmd ============\n%s\n=============================",
3898                 command.c_str());
3900     outbuf.clear();
3901     errbuf.clear();
3902     
3904     /*
3905     I really hate having win32 code in this program, but the
3906     read buffer in command.com and cmd.exe are just too small
3907     for the large commands we need for compiling and linking.
3908     */
3910     bool ret = true;
3912     //# Allocate a separate buffer for safety
3913     char *paramBuf = new char[command.size() + 1];
3914     if (!paramBuf)
3915        {
3916        error("executeCommand cannot allocate command buffer");
3917        return false;
3918        }
3919     strcpy(paramBuf, (char *)command.c_str());
3921     //# Go to http://msdn2.microsoft.com/en-us/library/ms682499.aspx
3922     //# to see how Win32 pipes work
3924     //# Create pipes
3925     SECURITY_ATTRIBUTES saAttr; 
3926     saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); 
3927     saAttr.bInheritHandle = TRUE; 
3928     saAttr.lpSecurityDescriptor = NULL; 
3929     HANDLE stdinRead,  stdinWrite;
3930     HANDLE stdoutRead, stdoutWrite;
3931     HANDLE stderrRead, stderrWrite;
3932     if (!CreatePipe(&stdinRead, &stdinWrite, &saAttr, 0))
3933         {
3934         error("executeProgram: could not create pipe");
3935         delete[] paramBuf;
3936         return false;
3937         } 
3938     SetHandleInformation(stdinWrite, HANDLE_FLAG_INHERIT, 0);
3939     if (!CreatePipe(&stdoutRead, &stdoutWrite, &saAttr, 0))
3940         {
3941         error("executeProgram: could not create pipe");
3942         delete[] paramBuf;
3943         return false;
3944         } 
3945     SetHandleInformation(stdoutRead, HANDLE_FLAG_INHERIT, 0);
3946     if (!CreatePipe(&stderrRead, &stderrWrite, &saAttr, 0))
3947         {
3948         error("executeProgram: could not create pipe");
3949         delete[] paramBuf;
3950         return false;
3951         } 
3952     SetHandleInformation(stderrRead, HANDLE_FLAG_INHERIT, 0);
3954     // Create the process
3955     STARTUPINFO siStartupInfo;
3956     PROCESS_INFORMATION piProcessInfo;
3957     memset(&siStartupInfo, 0, sizeof(siStartupInfo));
3958     memset(&piProcessInfo, 0, sizeof(piProcessInfo));
3959     siStartupInfo.cb = sizeof(siStartupInfo);
3960     siStartupInfo.hStdError   =  stderrWrite;
3961     siStartupInfo.hStdOutput  =  stdoutWrite;
3962     siStartupInfo.hStdInput   =  stdinRead;
3963     siStartupInfo.dwFlags    |=  STARTF_USESTDHANDLES;
3964    
3965     if (!CreateProcess(NULL, paramBuf, NULL, NULL, true,
3966                 0, NULL, NULL, &siStartupInfo,
3967                 &piProcessInfo))
3968         {
3969         error("executeCommand : could not create process : %s",
3970                     win32LastError().c_str());
3971         ret = false;
3972         }
3974     delete[] paramBuf;
3976     DWORD bytesWritten;
3977     if (inbuf.size()>0 &&
3978         !WriteFile(stdinWrite, inbuf.c_str(), inbuf.size(), 
3979                &bytesWritten, NULL))
3980         {
3981         error("executeCommand: could not write to pipe");
3982         return false;
3983         }    
3984     if (!CloseHandle(stdinWrite))
3985         {          
3986         error("executeCommand: could not close write pipe");
3987         return false;
3988         }
3989     if (!CloseHandle(stdoutWrite))
3990         {
3991         error("executeCommand: could not close read pipe");
3992         return false;
3993         }
3994     if (!CloseHandle(stderrWrite))
3995         {
3996         error("executeCommand: could not close read pipe");
3997         return false;
3998         }
4000     bool lastLoop = false;
4001     while (true)
4002         {
4003         DWORD avail;
4004         DWORD bytesRead;
4005         char readBuf[4096];
4007         //trace("## stderr");
4008         PeekNamedPipe(stderrRead, NULL, 0, NULL, &avail, NULL);
4009         if (avail > 0)
4010             {
4011             bytesRead = 0;
4012             if (avail>4096) avail = 4096;
4013             ReadFile(stderrRead, readBuf, avail, &bytesRead, NULL);
4014             if (bytesRead > 0)
4015                 {
4016                 for (unsigned int i=0 ; i<bytesRead ; i++)
4017                     errbuf.push_back(readBuf[i]);
4018                 }
4019             }
4021         //trace("## stdout");
4022         PeekNamedPipe(stdoutRead, NULL, 0, NULL, &avail, NULL);
4023         if (avail > 0)
4024             {
4025             bytesRead = 0;
4026             if (avail>4096) avail = 4096;
4027             ReadFile(stdoutRead, readBuf, avail, &bytesRead, NULL);
4028             if (bytesRead > 0)
4029                 {
4030                 for (unsigned int i=0 ; i<bytesRead ; i++)
4031                     outbuf.push_back(readBuf[i]);
4032                 }
4033             }
4034             
4035         //Was this the final check after program done?
4036         if (lastLoop)
4037             break;
4039         DWORD exitCode;
4040         GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
4041         if (exitCode != STILL_ACTIVE)
4042             lastLoop = true;
4044         Sleep(10);
4045         }    
4046     //trace("outbuf:%s", outbuf.c_str());
4047     if (!CloseHandle(stdoutRead))
4048         {
4049         error("executeCommand: could not close read pipe");
4050         return false;
4051         }
4052     if (!CloseHandle(stderrRead))
4053         {
4054         error("executeCommand: could not close read pipe");
4055         return false;
4056         }
4058     DWORD exitCode;
4059     GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
4060     //trace("exit code:%d", exitCode);
4061     if (exitCode != 0)
4062         {
4063         ret = false;
4064         }
4065     
4066     CloseHandle(piProcessInfo.hProcess);
4067     CloseHandle(piProcessInfo.hThread);
4069     return ret;
4071
4073 #else  /*do it unix style*/
4075 #include <sys/wait.h>
4079 /**
4080  * Execute a system call, using pipes to send data to the
4081  * program's stdin,  and reading stdout and stderr.
4082  */
4083 bool MakeBase::executeCommand(const String &command,
4084                               const String &inbuf,
4085                               String &outbuf,
4086                               String &errbuf)
4089     status("============ cmd ============\n%s\n=============================",
4090                 command.c_str());
4092     outbuf.clear();
4093     errbuf.clear();
4094     
4096     int outfds[2];
4097     if (pipe(outfds) < 0)
4098         return false;
4099     int errfds[2];
4100     if (pipe(errfds) < 0)
4101         return false;
4102     int pid = fork();
4103     if (pid < 0)
4104         {
4105         close(outfds[0]);
4106         close(outfds[1]);
4107         close(errfds[0]);
4108         close(errfds[1]);
4109         error("launch of command '%s' failed : %s",
4110              command.c_str(), strerror(errno));
4111         return false;
4112         }
4113     else if (pid > 0) // parent
4114         {
4115         close(outfds[1]);
4116         close(errfds[1]);
4117         }
4118     else // == 0, child
4119         {
4120         close(outfds[0]);
4121         dup2(outfds[1], STDOUT_FILENO);
4122         close(outfds[1]);
4123         close(errfds[0]);
4124         dup2(errfds[1], STDERR_FILENO);
4125         close(errfds[1]);
4127         char *args[4];
4128         args[0] = (char *)"sh";
4129         args[1] = (char *)"-c";
4130         args[2] = (char *)command.c_str();
4131         args[3] = NULL;
4132         execv("/bin/sh", args);
4133         exit(EXIT_FAILURE);
4134         }
4136     String outb;
4137     String errb;
4139     int outRead = outfds[0];
4140     int errRead = errfds[0];
4141     int max = outRead;
4142     if (errRead > max)
4143         max = errRead;
4145     bool outOpen = true;
4146     bool errOpen = true;
4148     while (outOpen || errOpen)
4149         {
4150         char ch;
4151         fd_set fdset;
4152         FD_ZERO(&fdset);
4153         if (outOpen)
4154             FD_SET(outRead, &fdset);
4155         if (errOpen)
4156             FD_SET(errRead, &fdset);
4157         int ret = select(max+1, &fdset, NULL, NULL, NULL);
4158         if (ret < 0)
4159             break;
4160         if (FD_ISSET(outRead, &fdset))
4161             {
4162             if (read(outRead, &ch, 1) <= 0)
4163                 { outOpen = false; }
4164             else if (ch <= 0)
4165                 { /* outOpen = false; */ }
4166             else
4167                 { outb.push_back(ch); }
4168             }
4169         if (FD_ISSET(errRead, &fdset))
4170             {
4171             if (read(errRead, &ch, 1) <= 0)
4172                 { errOpen = false; }
4173             else if (ch <= 0)
4174                 { /* errOpen = false; */ }
4175             else
4176                 { errb.push_back(ch); }
4177             }
4178         }
4180     int childReturnValue;
4181     wait(&childReturnValue);
4183     close(outRead);
4184     close(errRead);
4186     outbuf = outb;
4187     errbuf = errb;
4189     if (childReturnValue != 0)
4190         {
4191         error("exec of command '%s' failed : %s",
4192              command.c_str(), strerror(childReturnValue));
4193         return false;
4194         }
4196     return true;
4197
4199 #endif
4204 bool MakeBase::listDirectories(const String &baseName,
4205                               const String &dirName,
4206                               std::vector<String> &res)
4208     res.push_back(dirName);
4209     String fullPath = baseName;
4210     if (dirName.size()>0)
4211         {
4212         fullPath.append("/");
4213         fullPath.append(dirName);
4214         }
4215     DIR *dir = opendir(fullPath.c_str());
4216     while (true)
4217         {
4218         struct dirent *de = readdir(dir);
4219         if (!de)
4220             break;
4222         //Get the directory member name
4223         String s = de->d_name;
4224         if (s.size() == 0 || s[0] == '.')
4225             continue;
4226         String childName = dirName;
4227         childName.append("/");
4228         childName.append(s);
4230         String fullChildPath = baseName;
4231         fullChildPath.append("/");
4232         fullChildPath.append(childName);
4233         struct stat finfo;
4234         String childNative = getNativePath(fullChildPath);
4235         if (cachedStat(childNative, &finfo)<0)
4236             {
4237             error("cannot stat file:%s", childNative.c_str());
4238             }
4239         else if (S_ISDIR(finfo.st_mode))
4240             {
4241             //trace("directory: %s", childName.c_str());
4242             if (!listDirectories(baseName, childName, res))
4243                 return false;
4244             }
4245         }
4246     closedir(dir);
4248     return true;
4252 bool MakeBase::listFiles(const String &baseDir,
4253                          const String &dirName,
4254                          std::vector<String> &res)
4256     String fullDir = baseDir;
4257     if (dirName.size()>0)
4258         {
4259         fullDir.append("/");
4260         fullDir.append(dirName);
4261         }
4262     String dirNative = getNativePath(fullDir);
4264     std::vector<String> subdirs;
4265     DIR *dir = opendir(dirNative.c_str());
4266     if (!dir)
4267         {
4268         error("Could not open directory %s : %s",
4269               dirNative.c_str(), strerror(errno));
4270         return false;
4271         }
4272     while (true)
4273         {
4274         struct dirent *de = readdir(dir);
4275         if (!de)
4276             break;
4278         //Get the directory member name
4279         String s = de->d_name;
4280         if (s.size() == 0 || s[0] == '.')
4281             continue;
4282         String childName;
4283         if (dirName.size()>0)
4284             {
4285             childName.append(dirName);
4286             childName.append("/");
4287             }
4288         childName.append(s);
4289         String fullChild = baseDir;
4290         fullChild.append("/");
4291         fullChild.append(childName);
4292         
4293         if (isDirectory(fullChild))
4294             {
4295             //trace("directory: %s", childName.c_str());
4296             if (!listFiles(baseDir, childName, res))
4297                 return false;
4298             continue;
4299             }
4300         else if (!isRegularFile(fullChild))
4301             {
4302             error("unknown file:%s", childName.c_str());
4303             return false;
4304             }
4306        //all done!
4307         res.push_back(childName);
4309         }
4310     closedir(dir);
4312     return true;
4316 /**
4317  * Several different classes extend MakeBase.  By "propRef", we mean
4318  * the one holding the properties.  Likely "Make" itself
4319  */
4320 bool MakeBase::listFiles(MakeBase &propRef, FileSet &fileSet)
4322     //before doing the list,  resolve any property references
4323     //that might have been specified in the directory name, such as ${src}
4324     String fsDir = fileSet.getDirectory();
4325     String dir;
4326     if (!propRef.getSubstitutions(fsDir, dir))
4327         return false;
4328     String baseDir = propRef.resolve(dir);
4329     std::vector<String> fileList;
4330     if (!listFiles(baseDir, "", fileList))
4331         return false;
4333     std::vector<String> includes = fileSet.getIncludes();
4334     std::vector<String> excludes = fileSet.getExcludes();
4336     std::vector<String> incs;
4337     std::vector<String>::iterator iter;
4339     std::sort(fileList.begin(), fileList.end());
4341     //If there are <includes>, then add files to the output
4342     //in the order of the include list
4343     if (includes.size()==0)
4344         incs = fileList;
4345     else
4346         {
4347         for (iter = includes.begin() ; iter != includes.end() ; iter++)
4348             {
4349             String &pattern = *iter;
4350             std::vector<String>::iterator siter;
4351             for (siter = fileList.begin() ; siter != fileList.end() ; siter++)
4352                 {
4353                 String s = *siter;
4354                 if (regexMatch(s, pattern))
4355                     {
4356                     //trace("INCLUDED:%s", s.c_str());
4357                     incs.push_back(s);
4358                     }
4359                 }
4360             }
4361         }
4363     //Now trim off the <excludes>
4364     std::vector<String> res;
4365     for (iter = incs.begin() ; iter != incs.end() ; iter++)
4366         {
4367         String s = *iter;
4368         bool skipme = false;
4369         std::vector<String>::iterator siter;
4370         for (siter = excludes.begin() ; siter != excludes.end() ; siter++)
4371             {
4372             String &pattern = *siter;
4373             if (regexMatch(s, pattern))
4374                 {
4375                 //trace("EXCLUDED:%s", s.c_str());
4376                 skipme = true;
4377                 break;
4378                 }
4379             }
4380         if (!skipme)
4381             res.push_back(s);
4382         }
4383         
4384     fileSet.setFiles(res);
4386     return true;
4390 /**
4391  * 0 == all, 1 = cflags, 2 = libs
4392  */ 
4393 bool MakeBase::pkgConfigRecursive(const String packageName,
4394                                   const String &path, 
4395                                   const String &prefix, 
4396                                   int query,
4397                                   String &result,
4398                                   std::set<String> &deplist) 
4400     PkgConfig pkgConfig;
4401     if (path.size() > 0)
4402         pkgConfig.setPath(path);
4403     if (prefix.size() > 0)
4404         pkgConfig.setPrefix(prefix);
4405     if (!pkgConfig.query(packageName))
4406         return false;
4407     if (query == 0)
4408         result = pkgConfig.getAll();
4409     else if (query == 1)
4410         result = pkgConfig.getCflags();
4411     else
4412         result = pkgConfig.getLibs();
4413     deplist.insert(packageName);
4414     std::vector<String> list = pkgConfig.getRequireList();
4415     for (unsigned int i = 0 ; i<list.size() ; i++)
4416         {
4417         String depPkgName = list[i];
4418         if (deplist.find(depPkgName) != deplist.end())
4419             continue;
4420         String val;
4421         if (!pkgConfigRecursive(depPkgName, path, prefix, query, val, deplist))
4422             {
4423             error("Based on 'requires' attribute of package '%s'", packageName.c_str());
4424             return false;
4425             }
4426         result.append(" ");
4427         result.append(val);
4428         }
4430     return true;
4433 bool MakeBase::pkgConfigQuery(const String &packageName, int query, String &result)
4435     std::set<String> deplist;
4436     String path = getProperty("pkg-config-path");
4437     if (path.size()>0)
4438         path = resolve(path);
4439     String prefix = getProperty("pkg-config-prefix");
4440     String val;
4441     if (!pkgConfigRecursive(packageName, path, prefix, query, val, deplist))
4442         return false;
4443     result = val;
4444     return true;
4449 /**
4450  * replace a variable ref like ${a} with a value
4451  */
4452 bool MakeBase::lookupProperty(const String &propertyName, String &result)
4454     String varname = propertyName;
4455     if (envPrefix.size() > 0 &&
4456         varname.compare(0, envPrefix.size(), envPrefix) == 0)
4457         {
4458         varname = varname.substr(envPrefix.size());
4459         char *envstr = getenv(varname.c_str());
4460         if (!envstr)
4461             {
4462             error("environment variable '%s' not defined", varname.c_str());
4463             return false;
4464             }
4465         result = envstr;
4466         }
4467     else if (pcPrefix.size() > 0 &&
4468         varname.compare(0, pcPrefix.size(), pcPrefix) == 0)
4469         {
4470         varname = varname.substr(pcPrefix.size());
4471         String val;
4472         if (!pkgConfigQuery(varname, 0, val))
4473             return false;
4474         result = val;
4475         }
4476     else if (pccPrefix.size() > 0 &&
4477         varname.compare(0, pccPrefix.size(), pccPrefix) == 0)
4478         {
4479         varname = varname.substr(pccPrefix.size());
4480         String val;
4481         if (!pkgConfigQuery(varname, 1, val))
4482             return false;
4483         result = val;
4484         }
4485     else if (pclPrefix.size() > 0 &&
4486         varname.compare(0, pclPrefix.size(), pclPrefix) == 0)
4487         {
4488         varname = varname.substr(pclPrefix.size());
4489         String val;
4490         if (!pkgConfigQuery(varname, 2, val))
4491             return false;
4492         result = val;
4493         }
4494     else
4495         {
4496         std::map<String, String>::iterator iter;
4497         iter = properties.find(varname);
4498         if (iter != properties.end())
4499             {
4500             result = iter->second;
4501             }
4502         else
4503             {
4504             error("property '%s' not found", varname.c_str());
4505             return false;
4506             }
4507         }
4508     return true;
4514 /**
4515  * Analyse a string, looking for any substitutions or other
4516  * things that need resolution 
4517  */
4518 bool MakeBase::getSubstitutionsRecursive(const String &str,
4519                                          String &result, int depth)
4521     if (depth > 10)
4522         {
4523         error("nesting of substitutions too deep (>10) for '%s'",
4524                         str.c_str());
4525         return false;
4526         }
4527     String s = trim(str);
4528     int len = (int)s.size();
4529     String val;
4530     for (int i=0 ; i<len ; i++)
4531         {
4532         char ch = s[i];
4533         if (ch == '$' && s[i+1] == '{')
4534             {
4535             String varname;
4536             int j = i+2;
4537             for ( ; j<len ; j++)
4538                 {
4539                 ch = s[j];
4540                 if (ch == '$' && s[j+1] == '{')
4541                     {
4542                     error("attribute %s cannot have nested variable references",
4543                            s.c_str());
4544                     return false;
4545                     }
4546                 else if (ch == '}')
4547                     {
4548                     varname = trim(varname);
4549                     String varval;
4550                     if (!lookupProperty(varname, varval))
4551                         return false;
4552                     String varval2;
4553                     //Now see if the answer has ${} in it, too
4554                     if (!getSubstitutionsRecursive(varval, varval2, depth + 1))
4555                         return false;
4556                     val.append(varval2);
4557                     break;
4558                     }
4559                 else
4560                     {
4561                     varname.push_back(ch);
4562                     }
4563                 }
4564             i = j;
4565             }
4566         else
4567             {
4568             val.push_back(ch);
4569             }
4570         }
4571     result = val;
4572     return true;
4575 /**
4576  * Analyse a string, looking for any substitutions or other
4577  * things that need resilution 
4578  */
4579 bool MakeBase::getSubstitutions(const String &str, String &result)
4581     return getSubstitutionsRecursive(str, result, 0);
4586 /**
4587  * replace variable refs like ${a} with their values
4588  * Assume that the string has already been syntax validated
4589  */
4590 String MakeBase::eval(const String &s, const String &defaultVal)
4592     if (s.size()==0)
4593         return defaultVal;
4594     String ret;
4595     if (getSubstitutions(s, ret))
4596         return ret;
4597     else
4598         return defaultVal;
4602 /**
4603  * replace variable refs like ${a} with their values
4604  * return true or false
4605  * Assume that the string has already been syntax validated
4606  */
4607 bool MakeBase::evalBool(const String &s, bool defaultVal)
4609     if (s.size()==0)
4610         return defaultVal;
4611     String val = eval(s, "false");
4612     if (val.size()==0)
4613         return defaultVal;
4614     if (val == "true" || val == "TRUE")
4615         return true;
4616     else
4617         return false;
4621 /**
4622  * Get a string attribute, testing it for proper syntax and
4623  * property names.
4624  */
4625 bool MakeBase::getAttribute(Element *elem, const String &name,
4626                                     String &result)
4628     String s = elem->getAttribute(name);
4629     String tmp;
4630     bool ret = getSubstitutions(s, tmp);
4631     if (ret)
4632         result = s;  //assign -if- ok
4633     return ret;
4637 /**
4638  * Get a string value, testing it for proper syntax and
4639  * property names.
4640  */
4641 bool MakeBase::getValue(Element *elem, String &result)
4643     String s = elem->getValue();
4644     String tmp;
4645     bool ret = getSubstitutions(s, tmp);
4646     if (ret)
4647         result = s;  //assign -if- ok
4648     return ret;
4654 /**
4655  * Parse a <patternset> entry
4656  */  
4657 bool MakeBase::parsePatternSet(Element *elem,
4658                           MakeBase &propRef,
4659                           std::vector<String> &includes,
4660                           std::vector<String> &excludes
4661                           )
4663     std::vector<Element *> children  = elem->getChildren();
4664     for (unsigned int i=0 ; i<children.size() ; i++)
4665         {
4666         Element *child = children[i];
4667         String tagName = child->getName();
4668         if (tagName == "exclude")
4669             {
4670             String fname;
4671             if (!propRef.getAttribute(child, "name", fname))
4672                 return false;
4673             //trace("EXCLUDE: %s", fname.c_str());
4674             excludes.push_back(fname);
4675             }
4676         else if (tagName == "include")
4677             {
4678             String fname;
4679             if (!propRef.getAttribute(child, "name", fname))
4680                 return false;
4681             //trace("INCLUDE: %s", fname.c_str());
4682             includes.push_back(fname);
4683             }
4684         }
4686     return true;
4692 /**
4693  * Parse a <fileset> entry, and determine which files
4694  * should be included
4695  */  
4696 bool MakeBase::parseFileSet(Element *elem,
4697                           MakeBase &propRef,
4698                           FileSet &fileSet)
4700     String name = elem->getName();
4701     if (name != "fileset")
4702         {
4703         error("expected <fileset>");
4704         return false;
4705         }
4708     std::vector<String> includes;
4709     std::vector<String> excludes;
4711     //A fileset has one implied patternset
4712     if (!parsePatternSet(elem, propRef, includes, excludes))
4713         {
4714         return false;
4715         }
4716     //Look for child tags, including more patternsets
4717     std::vector<Element *> children  = elem->getChildren();
4718     for (unsigned int i=0 ; i<children.size() ; i++)
4719         {
4720         Element *child = children[i];
4721         String tagName = child->getName();
4722         if (tagName == "patternset")
4723             {
4724             if (!parsePatternSet(child, propRef, includes, excludes))
4725                 {
4726                 return false;
4727                 }
4728             }
4729         }
4731     String dir;
4732     //Now do the stuff
4733     //Get the base directory for reading file names
4734     if (!propRef.getAttribute(elem, "dir", dir))
4735         return false;
4737     fileSet.setDirectory(dir);
4738     fileSet.setIncludes(includes);
4739     fileSet.setExcludes(excludes);
4740     
4741     /*
4742     std::vector<String> fileList;
4743     if (dir.size() > 0)
4744         {
4745         String baseDir = propRef.resolve(dir);
4746         if (!listFiles(baseDir, "", includes, excludes, fileList))
4747             return false;
4748         }
4749     std::sort(fileList.begin(), fileList.end());
4750     result = fileList;
4751     */
4753     
4754     /*
4755     for (unsigned int i=0 ; i<result.size() ; i++)
4756         {
4757         trace("RES:%s", result[i].c_str());
4758         }
4759     */
4761     
4762     return true;
4765 /**
4766  * Parse a <filelist> entry.  This is far simpler than FileSet,
4767  * since no directory scanning is needed.  The file names are listed
4768  * explicitly.
4769  */  
4770 bool MakeBase::parseFileList(Element *elem,
4771                           MakeBase &propRef,
4772                           FileList &fileList)
4774     std::vector<String> fnames;
4775     //Look for child tags, namely "file"
4776     std::vector<Element *> children  = elem->getChildren();
4777     for (unsigned int i=0 ; i<children.size() ; i++)
4778         {
4779         Element *child = children[i];
4780         String tagName = child->getName();
4781         if (tagName == "file")
4782             {
4783             String fname = child->getAttribute("name");
4784             if (fname.size()==0)
4785                 {
4786                 error("<file> element requires name="" attribute");
4787                 return false;
4788                 }
4789             fnames.push_back(fname);
4790             }
4791         else
4792             {
4793             error("tag <%s> not allowed in <fileset>", tagName.c_str());
4794             return false;
4795             }
4796         }
4798     String dir;
4799     //Get the base directory for reading file names
4800     if (!propRef.getAttribute(elem, "dir", dir))
4801         return false;
4802     fileList.setDirectory(dir);
4803     fileList.setFiles(fnames);
4805     return true;
4810 /**
4811  * Create a directory, making intermediate dirs
4812  * if necessary
4813  */                  
4814 bool MakeBase::createDirectory(const String &dirname)
4816     //trace("## createDirectory: %s", dirname.c_str());
4817     //## first check if it exists
4818     struct stat finfo;
4819     String nativeDir = getNativePath(dirname);
4820     char *cnative = (char *) nativeDir.c_str();
4821 #ifdef __WIN32__
4822     if (strlen(cnative)==2 && cnative[1]==':')
4823         return true;
4824 #endif
4825     if (cachedStat(nativeDir, &finfo)==0)
4826         {
4827         if (!S_ISDIR(finfo.st_mode))
4828             {
4829             error("mkdir: file %s exists but is not a directory",
4830                   cnative);
4831             return false;
4832             }
4833         else //exists
4834             {
4835             return true;
4836             }
4837         }
4839     //## 2: pull off the last path segment, if any,
4840     //## to make the dir 'above' this one, if necessary
4841     unsigned int pos = dirname.find_last_of('/');
4842     if (pos>0 && pos != dirname.npos)
4843         {
4844         String subpath = dirname.substr(0, pos);
4845         //A letter root (c:) ?
4846         if (!createDirectory(subpath))
4847             return false;
4848         }
4849         
4850     //## 3: now make
4851 #ifdef __WIN32__
4852     if (mkdir(cnative)<0)
4853 #else
4854     if (mkdir(cnative, S_IRWXU | S_IRWXG | S_IRWXO)<0)
4855 #endif
4856         {
4857         error("cannot make directory '%s' : %s",
4858                  cnative, strerror(errno));
4859         return false;
4860         }
4861         
4862     return true;
4866 /**
4867  * Remove a directory recursively
4868  */ 
4869 bool MakeBase::removeDirectory(const String &dirName)
4871     char *dname = (char *)dirName.c_str();
4873     DIR *dir = opendir(dname);
4874     if (!dir)
4875         {
4876         //# Let this fail nicely.
4877         return true;
4878         //error("error opening directory %s : %s", dname, strerror(errno));
4879         //return false;
4880         }
4881     
4882     while (true)
4883         {
4884         struct dirent *de = readdir(dir);
4885         if (!de)
4886             break;
4888         //Get the directory member name
4889         String s = de->d_name;
4890         if (s.size() == 0 || s[0] == '.')
4891             continue;
4892         String childName;
4893         if (dirName.size() > 0)
4894             {
4895             childName.append(dirName);
4896             childName.append("/");
4897             }
4898         childName.append(s);
4901         struct stat finfo;
4902         String childNative = getNativePath(childName);
4903         char *cnative = (char *)childNative.c_str();
4904         if (cachedStat(childNative, &finfo)<0)
4905             {
4906             error("cannot stat file:%s", cnative);
4907             }
4908         else if (S_ISDIR(finfo.st_mode))
4909             {
4910             //trace("DEL dir: %s", childName.c_str());
4911             if (!removeDirectory(childName))
4912                 {
4913                 return false;
4914                 }
4915             }
4916         else if (!S_ISREG(finfo.st_mode))
4917             {
4918             //trace("not regular: %s", cnative);
4919             }
4920         else
4921             {
4922             //trace("DEL file: %s", childName.c_str());
4923             if (remove(cnative)<0)
4924                 {
4925                 error("error deleting %s : %s",
4926                      cnative, strerror(errno));
4927                 return false;
4928                 }
4929             }
4930         }
4931     closedir(dir);
4933     //Now delete the directory
4934     String native = getNativePath(dirName);
4935     if (rmdir(native.c_str())<0)
4936         {
4937         error("could not delete directory %s : %s",
4938             native.c_str() , strerror(errno));
4939         return false;
4940         }
4942     return true;
4943     
4947 /**
4948  * Copy a file from one name to another. Perform only if needed
4949  */ 
4950 bool MakeBase::copyFile(const String &srcFile, const String &destFile)
4952     //# 1 Check up-to-date times
4953     String srcNative = getNativePath(srcFile);
4954     struct stat srcinfo;
4955     if (cachedStat(srcNative, &srcinfo)<0)
4956         {
4957         error("source file %s for copy does not exist",
4958                  srcNative.c_str());
4959         return false;
4960         }
4962     String destNative = getNativePath(destFile);
4963     struct stat destinfo;
4964     if (cachedStat(destNative, &destinfo)==0)
4965         {
4966         if (destinfo.st_mtime >= srcinfo.st_mtime)
4967             return true;
4968         }
4969         
4970     //# 2 prepare a destination directory if necessary
4971     unsigned int pos = destFile.find_last_of('/');
4972     if (pos != destFile.npos)
4973         {
4974         String subpath = destFile.substr(0, pos);
4975         if (!createDirectory(subpath))
4976             return false;
4977         }
4979     //# 3 do the data copy
4980 #ifndef __WIN32__
4982     FILE *srcf = fopen(srcNative.c_str(), "rb");
4983     if (!srcf)
4984         {
4985         error("copyFile cannot open '%s' for reading", srcNative.c_str());
4986         return false;
4987         }
4988     FILE *destf = fopen(destNative.c_str(), "wb");
4989     if (!destf)
4990         {
4991         error("copyFile cannot open %s for writing", srcNative.c_str());
4992         return false;
4993         }
4995     while (!feof(srcf))
4996         {
4997         int ch = fgetc(srcf);
4998         if (ch<0)
4999             break;
5000         fputc(ch, destf);
5001         }
5003     fclose(destf);
5004     fclose(srcf);
5006 #else
5007     
5008     if (!CopyFile(srcNative.c_str(), destNative.c_str(), false))
5009         {
5010         error("copyFile from %s to %s failed",
5011              srcNative.c_str(), destNative.c_str());
5012         return false;
5013         }
5014         
5015 #endif /* __WIN32__ */
5018     return true;
5023 /**
5024  * Tests if the file exists and is a regular file
5025  */ 
5026 bool MakeBase::isRegularFile(const String &fileName)
5028     String native = getNativePath(fileName);
5029     struct stat finfo;
5030     
5031     //Exists?
5032     if (cachedStat(native, &finfo)<0)
5033         return false;
5036     //check the file mode
5037     if (!S_ISREG(finfo.st_mode))
5038         return false;
5040     return true;
5043 /**
5044  * Tests if the file exists and is a directory
5045  */ 
5046 bool MakeBase::isDirectory(const String &fileName)
5048     String native = getNativePath(fileName);
5049     struct stat finfo;
5050     
5051     //Exists?
5052     if (cachedStat(native, &finfo)<0)
5053         return false;
5056     //check the file mode
5057     if (!S_ISDIR(finfo.st_mode))
5058         return false;
5060     return true;
5065 /**
5066  * Tests is the modification of fileA is newer than fileB
5067  */ 
5068 bool MakeBase::isNewerThan(const String &fileA, const String &fileB)
5070     //trace("isNewerThan:'%s' , '%s'", fileA.c_str(), fileB.c_str());
5071     String nativeA = getNativePath(fileA);
5072     struct stat infoA;
5073     //IF source does not exist, NOT newer
5074     if (cachedStat(nativeA, &infoA)<0)
5075         {
5076         return false;
5077         }
5079     String nativeB = getNativePath(fileB);
5080     struct stat infoB;
5081     //IF dest does not exist, YES, newer
5082     if (cachedStat(nativeB, &infoB)<0)
5083         {
5084         return true;
5085         }
5087     //check the actual times
5088     if (infoA.st_mtime > infoB.st_mtime)
5089         {
5090         return true;
5091         }
5093     return false;
5097 //########################################################################
5098 //# P K G    C O N F I G
5099 //########################################################################
5102 /**
5103  * Get a character from the buffer at pos.  If out of range,
5104  * return -1 for safety
5105  */
5106 int PkgConfig::get(int pos)
5108     if (pos>parselen)
5109         return -1;
5110     return parsebuf[pos];
5115 /**
5116  *  Skip over all whitespace characters beginning at pos.  Return
5117  *  the position of the first non-whitespace character.
5118  *  Pkg-config is line-oriented, so check for newline
5119  */
5120 int PkgConfig::skipwhite(int pos)
5122     while (pos < parselen)
5123         {
5124         int ch = get(pos);
5125         if (ch < 0)
5126             break;
5127         if (!isspace(ch))
5128             break;
5129         pos++;
5130         }
5131     return pos;
5135 /**
5136  *  Parse the buffer beginning at pos, for a word.  Fill
5137  *  'ret' with the result.  Return the position after the
5138  *  word.
5139  */
5140 int PkgConfig::getword(int pos, String &ret)
5142     while (pos < parselen)
5143         {
5144         int ch = get(pos);
5145         if (ch < 0)
5146             break;
5147         if (!isalnum(ch) && ch != '_' && ch != '-' && ch != '+' && ch != '.')
5148             break;
5149         ret.push_back((char)ch);
5150         pos++;
5151         }
5152     return pos;
5155 bool PkgConfig::parseRequires()
5157     if (requires.size() == 0)
5158         return true;
5159     parsebuf = (char *)requires.c_str();
5160     parselen = requires.size();
5161     int pos = 0;
5162     while (pos < parselen)
5163         {
5164         pos = skipwhite(pos);
5165         String val;
5166         int pos2 = getword(pos, val);
5167         if (pos2 == pos)
5168             break;
5169         pos = pos2;
5170         //trace("val %s", val.c_str());
5171         requireList.push_back(val);
5172         }
5173     return true;
5177 static int getint(const String str)
5179     char *s = (char *)str.c_str();
5180     char *ends = NULL;
5181     long val = strtol(s, &ends, 10);
5182     if (ends == s)
5183         return 0L;
5184     else
5185         return val;
5188 void PkgConfig::parseVersion()
5190     if (version.size() == 0)
5191         return;
5192     String s1, s2, s3;
5193     unsigned int pos = 0;
5194     unsigned int pos2 = version.find('.', pos);
5195     if (pos2 == version.npos)
5196         {
5197         s1 = version;
5198         }
5199     else
5200         {
5201         s1 = version.substr(pos, pos2-pos);
5202         pos = pos2;
5203         pos++;
5204         if (pos < version.size())
5205             {
5206             pos2 = version.find('.', pos);
5207             if (pos2 == version.npos)
5208                 {
5209                 s2 = version.substr(pos, version.size()-pos);
5210                 }
5211             else
5212                 {
5213                 s2 = version.substr(pos, pos2-pos);
5214                 pos = pos2;
5215                 pos++;
5216                 if (pos < version.size())
5217                     s3 = version.substr(pos, pos2-pos);
5218                 }
5219             }
5220         }
5222     majorVersion = getint(s1);
5223     minorVersion = getint(s2);
5224     microVersion = getint(s3);
5225     //trace("version:%d.%d.%d", majorVersion,
5226     //          minorVersion, microVersion );
5230 bool PkgConfig::parseLine(const String &lineBuf)
5232     parsebuf = (char *)lineBuf.c_str();
5233     parselen = lineBuf.size();
5234     int pos = 0;
5235     
5236     while (pos < parselen)
5237         {
5238         String attrName;
5239         pos = skipwhite(pos);
5240         int ch = get(pos);
5241         if (ch == '#')
5242             {
5243             //comment.  eat the rest of the line
5244             while (pos < parselen)
5245                 {
5246                 ch = get(pos);
5247                 if (ch == '\n' || ch < 0)
5248                     break;
5249                 pos++;
5250                 }
5251             continue;
5252             }
5253         pos = getword(pos, attrName);
5254         if (attrName.size() == 0)
5255             continue;
5256         
5257         pos = skipwhite(pos);
5258         ch = get(pos);
5259         if (ch != ':' && ch != '=')
5260             {
5261             error("expected ':' or '='");
5262             return false;
5263             }
5264         pos++;
5265         pos = skipwhite(pos);
5266         String attrVal;
5267         while (pos < parselen)
5268             {
5269             ch = get(pos);
5270             if (ch == '\n' || ch < 0)
5271                 break;
5272             else if (ch == '$' && get(pos+1) == '{')
5273                 {
5274                 //#  this is a ${substitution}
5275                 pos += 2;
5276                 String subName;
5277                 while (pos < parselen)
5278                     {
5279                     ch = get(pos);
5280                     if (ch < 0)
5281                         {
5282                         error("unterminated substitution");
5283                         return false;
5284                         }
5285                     else if (ch == '}')
5286                         break;
5287                     else
5288                         subName.push_back((char)ch);
5289                     pos++;
5290                     }
5291                 //trace("subName:%s %s", subName.c_str(), prefix.c_str());
5292                 if (subName == "prefix" && prefix.size()>0)
5293                     {
5294                     attrVal.append(prefix);
5295                     //trace("prefix override:%s", prefix.c_str());
5296                     }
5297                 else
5298                     {
5299                     String subVal = attrs[subName];
5300                     //trace("subVal:%s", subVal.c_str());
5301                     attrVal.append(subVal);
5302                     }
5303                 }
5304             else
5305                 attrVal.push_back((char)ch);
5306             pos++;
5307             }
5309         attrVal = trim(attrVal);
5310         attrs[attrName] = attrVal;
5312         String attrNameL = toLower(attrName);
5314         if (attrNameL == "name")
5315             name = attrVal;
5316         else if (attrNameL == "description")
5317             description = attrVal;
5318         else if (attrNameL == "cflags")
5319             cflags = attrVal;
5320         else if (attrNameL == "libs")
5321             libs = attrVal;
5322         else if (attrNameL == "requires")
5323             requires = attrVal;
5324         else if (attrNameL == "version")
5325             version = attrVal;
5327         //trace("name:'%s'  value:'%s'",
5328         //      attrName.c_str(), attrVal.c_str());
5329         }
5331     return true;
5335 bool PkgConfig::parse(const String &buf)
5337     init();
5339     String line;
5340     int lineNr = 0;
5341     for (unsigned int p=0 ; p<buf.size() ; p++)
5342         {
5343         int ch = buf[p];
5344         if (ch == '\n' || ch == '\r')
5345             {
5346             if (!parseLine(line))
5347                 return false;
5348             line.clear();
5349             lineNr++;
5350             }
5351         else
5352             {
5353             line.push_back(ch);
5354             }
5355         }
5356     if (line.size()>0)
5357         {
5358         if (!parseLine(line))
5359             return false;
5360         }
5362     parseRequires();
5363     parseVersion();
5365     return true;
5371 void PkgConfig::dumpAttrs()
5373     //trace("### PkgConfig attributes for %s", fileName.c_str());
5374     std::map<String, String>::iterator iter;
5375     for (iter=attrs.begin() ; iter!=attrs.end() ; iter++)
5376         {
5377         trace("   %s = %s", iter->first.c_str(), iter->second.c_str());
5378         }
5382 bool PkgConfig::readFile(const String &fname)
5384     fileName = getNativePath(fname);
5386     FILE *f = fopen(fileName.c_str(), "r");
5387     if (!f)
5388         {
5389         error("cannot open file '%s' for reading", fileName.c_str());
5390         return false;
5391         }
5392     String buf;
5393     while (true)
5394         {
5395         int ch = fgetc(f);
5396         if (ch < 0)
5397             break;
5398         buf.push_back((char)ch);
5399         }
5400     fclose(f);
5402     //trace("####### File:\n%s", buf.c_str());
5403     if (!parse(buf))
5404         {
5405         return false;
5406         }
5408     //dumpAttrs();
5410     return true;
5415 bool PkgConfig::query(const String &pkgName)
5417     name = pkgName;
5419     String fname = path;
5420     fname.append("/");
5421     fname.append(name);
5422     fname.append(".pc");
5424     if (!readFile(fname))
5425         {
5426         error("Cannot find package '%s'. Do you have it installed?",
5427                        pkgName.c_str());
5428         return false;
5429         }
5430     
5431     return true;
5438 //########################################################################
5439 //# D E P T O O L
5440 //########################################################################
5444 /**
5445  *  Class which holds information for each file.
5446  */
5447 class FileRec
5449 public:
5451     typedef enum
5452         {
5453         UNKNOWN,
5454         CFILE,
5455         HFILE,
5456         OFILE
5457         } FileType;
5459     /**
5460      *  Constructor
5461      */
5462     FileRec()
5463         { init(); type = UNKNOWN; }
5465     /**
5466      *  Copy constructor
5467      */
5468     FileRec(const FileRec &other)
5469         { init(); assign(other); }
5470     /**
5471      *  Constructor
5472      */
5473     FileRec(int typeVal)
5474         { init(); type = typeVal; }
5475     /**
5476      *  Assignment operator
5477      */
5478     FileRec &operator=(const FileRec &other)
5479         { init(); assign(other); return *this; }
5482     /**
5483      *  Destructor
5484      */
5485     ~FileRec()
5486         {}
5488     /**
5489      *  Directory part of the file name
5490      */
5491     String path;
5493     /**
5494      *  Base name, sans directory and suffix
5495      */
5496     String baseName;
5498     /**
5499      *  File extension, such as cpp or h
5500      */
5501     String suffix;
5503     /**
5504      *  Type of file: CFILE, HFILE, OFILE
5505      */
5506     int type;
5508     /**
5509      * Used to list files ref'd by this one
5510      */
5511     std::map<String, FileRec *> files;
5514 private:
5516     void init()
5517         {
5518         }
5520     void assign(const FileRec &other)
5521         {
5522         type     = other.type;
5523         baseName = other.baseName;
5524         suffix   = other.suffix;
5525         files    = other.files;
5526         }
5528 };
5532 /**
5533  *  Simpler dependency record
5534  */
5535 class DepRec
5537 public:
5539     /**
5540      *  Constructor
5541      */
5542     DepRec()
5543         {init();}
5545     /**
5546      *  Copy constructor
5547      */
5548     DepRec(const DepRec &other)
5549         {init(); assign(other);}
5550     /**
5551      *  Constructor
5552      */
5553     DepRec(const String &fname)
5554         {init(); name = fname; }
5555     /**
5556      *  Assignment operator
5557      */
5558     DepRec &operator=(const DepRec &other)
5559         {init(); assign(other); return *this;}
5562     /**
5563      *  Destructor
5564      */
5565     ~DepRec()
5566         {}
5568     /**
5569      *  Directory part of the file name
5570      */
5571     String path;
5573     /**
5574      *  Base name, without the path and suffix
5575      */
5576     String name;
5578     /**
5579      *  Suffix of the source
5580      */
5581     String suffix;
5584     /**
5585      * Used to list files ref'd by this one
5586      */
5587     std::vector<String> files;
5590 private:
5592     void init()
5593         {
5594         }
5596     void assign(const DepRec &other)
5597         {
5598         path     = other.path;
5599         name     = other.name;
5600         suffix   = other.suffix;
5601         files    = other.files; //avoid recursion
5602         }
5604 };
5607 class DepTool : public MakeBase
5609 public:
5611     /**
5612      *  Constructor
5613      */
5614     DepTool()
5615         { init(); }
5617     /**
5618      *  Copy constructor
5619      */
5620     DepTool(const DepTool &other)
5621         { init(); assign(other); }
5623     /**
5624      *  Assignment operator
5625      */
5626     DepTool &operator=(const DepTool &other)
5627         { init(); assign(other); return *this; }
5630     /**
5631      *  Destructor
5632      */
5633     ~DepTool()
5634         {}
5637     /**
5638      *  Reset this section of code
5639      */
5640     virtual void init();
5641     
5642     /**
5643      *  Reset this section of code
5644      */
5645     virtual void assign(const DepTool &other)
5646         {
5647         }
5648     
5649     /**
5650      *  Sets the source directory which will be scanned
5651      */
5652     virtual void setSourceDirectory(const String &val)
5653         { sourceDir = val; }
5655     /**
5656      *  Returns the source directory which will be scanned
5657      */
5658     virtual String getSourceDirectory()
5659         { return sourceDir; }
5661     /**
5662      *  Sets the list of files within the directory to analyze
5663      */
5664     virtual void setFileList(const std::vector<String> &list)
5665         { fileList = list; }
5667     /**
5668      * Creates the list of all file names which will be
5669      * candidates for further processing.  Reads make.exclude
5670      * to see which files for directories to leave out.
5671      */
5672     virtual bool createFileList();
5675     /**
5676      *  Generates the forward dependency list
5677      */
5678     virtual bool generateDependencies();
5681     /**
5682      *  Generates the forward dependency list, saving the file
5683      */
5684     virtual bool generateDependencies(const String &);
5687     /**
5688      *  Load a dependency file
5689      */
5690     std::vector<DepRec> loadDepFile(const String &fileName);
5692     /**
5693      *  Load a dependency file, generating one if necessary
5694      */
5695     std::vector<DepRec> getDepFile(const String &fileName,
5696               bool forceRefresh);
5698     /**
5699      *  Save a dependency file
5700      */
5701     bool saveDepFile(const String &fileName);
5704 private:
5707     /**
5708      *
5709      */
5710     void parseName(const String &fullname,
5711                    String &path,
5712                    String &basename,
5713                    String &suffix);
5715     /**
5716      *
5717      */
5718     int get(int pos);
5720     /**
5721      *
5722      */
5723     int skipwhite(int pos);
5725     /**
5726      *
5727      */
5728     int getword(int pos, String &ret);
5730     /**
5731      *
5732      */
5733     bool sequ(int pos, const char *key);
5735     /**
5736      *
5737      */
5738     bool addIncludeFile(FileRec *frec, const String &fname);
5740     /**
5741      *
5742      */
5743     bool scanFile(const String &fname, FileRec *frec);
5745     /**
5746      *
5747      */
5748     bool processDependency(FileRec *ofile, FileRec *include);
5750     /**
5751      *
5752      */
5753     String sourceDir;
5755     /**
5756      *
5757      */
5758     std::vector<String> fileList;
5760     /**
5761      *
5762      */
5763     std::vector<String> directories;
5765     /**
5766      * A list of all files which will be processed for
5767      * dependencies.
5768      */
5769     std::map<String, FileRec *> allFiles;
5771     /**
5772      * The list of .o files, and the
5773      * dependencies upon them.
5774      */
5775     std::map<String, FileRec *> oFiles;
5777     int depFileSize;
5778     char *depFileBuf;
5780     static const int readBufSize = 8192;
5781     char readBuf[8193];//byte larger
5783 };
5789 /**
5790  *  Clean up after processing.  Called by the destructor, but should
5791  *  also be called before the object is reused.
5792  */
5793 void DepTool::init()
5795     sourceDir = ".";
5797     fileList.clear();
5798     directories.clear();
5799     
5800     //clear output file list
5801     std::map<String, FileRec *>::iterator iter;
5802     for (iter=oFiles.begin(); iter!=oFiles.end() ; iter++)
5803         delete iter->second;
5804     oFiles.clear();
5806     //allFiles actually contains the master copies. delete them
5807     for (iter= allFiles.begin(); iter!=allFiles.end() ; iter++)
5808         delete iter->second;
5809     allFiles.clear(); 
5816 /**
5817  *  Parse a full path name into path, base name, and suffix
5818  */
5819 void DepTool::parseName(const String &fullname,
5820                         String &path,
5821                         String &basename,
5822                         String &suffix)
5824     if (fullname.size() < 2)
5825         return;
5827     unsigned int pos = fullname.find_last_of('/');
5828     if (pos != fullname.npos && pos<fullname.size()-1)
5829         {
5830         path = fullname.substr(0, pos);
5831         pos++;
5832         basename = fullname.substr(pos, fullname.size()-pos);
5833         }
5834     else
5835         {
5836         path = "";
5837         basename = fullname;
5838         }
5840     pos = basename.find_last_of('.');
5841     if (pos != basename.npos && pos<basename.size()-1)
5842         {
5843         suffix   = basename.substr(pos+1, basename.size()-pos-1);
5844         basename = basename.substr(0, pos);
5845         }
5847     //trace("parsename:%s %s %s", path.c_str(),
5848     //        basename.c_str(), suffix.c_str()); 
5853 /**
5854  *  Generate our internal file list.
5855  */
5856 bool DepTool::createFileList()
5859     for (unsigned int i=0 ; i<fileList.size() ; i++)
5860         {
5861         String fileName = fileList[i];
5862         //trace("## FileName:%s", fileName.c_str());
5863         String path;
5864         String basename;
5865         String sfx;
5866         parseName(fileName, path, basename, sfx);
5867         if (sfx == "cpp" || sfx == "c" || sfx == "cxx"   ||
5868             sfx == "cc" || sfx == "CC")
5869             {
5870             FileRec *fe         = new FileRec(FileRec::CFILE);
5871             fe->path            = path;
5872             fe->baseName        = basename;
5873             fe->suffix          = sfx;
5874             allFiles[fileName]  = fe;
5875             }
5876         else if (sfx == "h"   ||  sfx == "hh"  ||
5877                  sfx == "hpp" ||  sfx == "hxx")
5878             {
5879             FileRec *fe         = new FileRec(FileRec::HFILE);
5880             fe->path            = path;
5881             fe->baseName        = basename;
5882             fe->suffix          = sfx;
5883             allFiles[fileName]  = fe;
5884             }
5885         }
5887     if (!listDirectories(sourceDir, "", directories))
5888         return false;
5889         
5890     return true;
5897 /**
5898  * Get a character from the buffer at pos.  If out of range,
5899  * return -1 for safety
5900  */
5901 int DepTool::get(int pos)
5903     if (pos>depFileSize)
5904         return -1;
5905     return depFileBuf[pos];
5910 /**
5911  *  Skip over all whitespace characters beginning at pos.  Return
5912  *  the position of the first non-whitespace character.
5913  */
5914 int DepTool::skipwhite(int pos)
5916     while (pos < depFileSize)
5917         {
5918         int ch = get(pos);
5919         if (ch < 0)
5920             break;
5921         if (!isspace(ch))
5922             break;
5923         pos++;
5924         }
5925     return pos;
5929 /**
5930  *  Parse the buffer beginning at pos, for a word.  Fill
5931  *  'ret' with the result.  Return the position after the
5932  *  word.
5933  */
5934 int DepTool::getword(int pos, String &ret)
5936     while (pos < depFileSize)
5937         {
5938         int ch = get(pos);
5939         if (ch < 0)
5940             break;
5941         if (isspace(ch))
5942             break;
5943         ret.push_back((char)ch);
5944         pos++;
5945         }
5946     return pos;
5949 /**
5950  * Return whether the sequence of characters in the buffer
5951  * beginning at pos match the key,  for the length of the key
5952  */
5953 bool DepTool::sequ(int pos, const char *key)
5955     while (*key)
5956         {
5957         if (*key != get(pos))
5958             return false;
5959         key++; pos++;
5960         }
5961     return true;
5966 /**
5967  *  Add an include file name to a file record.  If the name
5968  *  is not found in allFiles explicitly, try prepending include
5969  *  directory names to it and try again.
5970  */
5971 bool DepTool::addIncludeFile(FileRec *frec, const String &iname)
5973     //# if the name is an exact match to a path name
5974     //# in allFiles, like "myinc.h"
5975     std::map<String, FileRec *>::iterator iter =
5976            allFiles.find(iname);
5977     if (iter != allFiles.end()) //already exists
5978         {
5979          //h file in same dir
5980         FileRec *other = iter->second;
5981         //trace("local: '%s'", iname.c_str());
5982         frec->files[iname] = other;
5983         return true;
5984         }
5985     else 
5986         {
5987         //## Ok, it was not found directly
5988         //look in other dirs
5989         std::vector<String>::iterator diter;
5990         for (diter=directories.begin() ;
5991              diter!=directories.end() ; diter++)
5992             {
5993             String dfname = *diter;
5994             dfname.append("/");
5995             dfname.append(iname);
5996             URI fullPathURI(dfname);  //normalize path name
5997             String fullPath = fullPathURI.getPath();
5998             if (fullPath[0] == '/')
5999                 fullPath = fullPath.substr(1);
6000             //trace("Normalized %s to %s", dfname.c_str(), fullPath.c_str());
6001             iter = allFiles.find(fullPath);
6002             if (iter != allFiles.end())
6003                 {
6004                 FileRec *other = iter->second;
6005                 //trace("other: '%s'", iname.c_str());
6006                 frec->files[fullPath] = other;
6007                 return true;
6008                 }
6009             }
6010         }
6011     return true;
6016 /**
6017  *  Lightly parse a file to find the #include directives.  Do
6018  *  a bit of state machine stuff to make sure that the directive
6019  *  is valid.  (Like not in a comment).
6020  */
6021 bool DepTool::scanFile(const String &fname, FileRec *frec)
6023     String fileName;
6024     if (sourceDir.size() > 0)
6025         {
6026         fileName.append(sourceDir);
6027         fileName.append("/");
6028         }
6029     fileName.append(fname);
6030     String nativeName = getNativePath(fileName);
6031     FILE *f = fopen(nativeName.c_str(), "r");
6032     if (!f)
6033         {
6034         error("Could not open '%s' for reading", fname.c_str());
6035         return false;
6036         }
6037     String buf;
6038     while (!feof(f))
6039         {
6040         int nrbytes = fread(readBuf, 1, readBufSize, f);
6041         readBuf[nrbytes] = '\0';
6042         buf.append(readBuf);
6043         }
6044     fclose(f);
6046     depFileSize = buf.size();
6047     depFileBuf  = (char *)buf.c_str();
6048     int pos = 0;
6051     while (pos < depFileSize)
6052         {
6053         //trace("p:%c", get(pos));
6055         //# Block comment
6056         if (get(pos) == '/' && get(pos+1) == '*')
6057             {
6058             pos += 2;
6059             while (pos < depFileSize)
6060                 {
6061                 if (get(pos) == '*' && get(pos+1) == '/')
6062                     {
6063                     pos += 2;
6064                     break;
6065                     }
6066                 else
6067                     pos++;
6068                 }
6069             }
6070         //# Line comment
6071         else if (get(pos) == '/' && get(pos+1) == '/')
6072             {
6073             pos += 2;
6074             while (pos < depFileSize)
6075                 {
6076                 if (get(pos) == '\n')
6077                     {
6078                     pos++;
6079                     break;
6080                     }
6081                 else
6082                     pos++;
6083                 }
6084             }
6085         //# #include! yaay
6086         else if (sequ(pos, "#include"))
6087             {
6088             pos += 8;
6089             pos = skipwhite(pos);
6090             String iname;
6091             pos = getword(pos, iname);
6092             if (iname.size()>2)
6093                 {
6094                 iname = iname.substr(1, iname.size()-2);
6095                 addIncludeFile(frec, iname);
6096                 }
6097             }
6098         else
6099             {
6100             pos++;
6101             }
6102         }
6104     return true;
6109 /**
6110  *  Recursively check include lists to find all files in allFiles to which
6111  *  a given file is dependent.
6112  */
6113 bool DepTool::processDependency(FileRec *ofile, FileRec *include)
6115     std::map<String, FileRec *>::iterator iter;
6116     for (iter=include->files.begin() ; iter!=include->files.end() ; iter++)
6117         {
6118         String fname  = iter->first;
6119         if (ofile->files.find(fname) != ofile->files.end())
6120             {
6121             //trace("file '%s' already seen", fname.c_str());
6122             continue;
6123             }
6124         FileRec *child  = iter->second;
6125         ofile->files[fname] = child;
6126       
6127         processDependency(ofile, child);
6128         }
6131     return true;
6138 /**
6139  *  Generate the file dependency list.
6140  */
6141 bool DepTool::generateDependencies()
6143     std::map<String, FileRec *>::iterator iter;
6144     //# First pass.  Scan for all includes
6145     for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
6146         {
6147         FileRec *frec = iter->second;
6148         if (!scanFile(iter->first, frec))
6149             {
6150             //quit?
6151             }
6152         }
6154     //# Second pass.  Scan for all includes
6155     for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
6156         {
6157         FileRec *include = iter->second;
6158         if (include->type == FileRec::CFILE)
6159             {
6160             //String cFileName   = iter->first;
6161             FileRec *ofile     = new FileRec(FileRec::OFILE);
6162             ofile->path        = include->path;
6163             ofile->baseName    = include->baseName;
6164             ofile->suffix      = include->suffix;
6165             String fname       = include->path;
6166             if (fname.size()>0)
6167                 fname.append("/");
6168             fname.append(include->baseName);
6169             fname.append(".o");
6170             oFiles[fname]    = ofile;
6171             //add the .c file first?   no, don't
6172             //ofile->files[cFileName] = include;
6173             
6174             //trace("ofile:%s", fname.c_str());
6176             processDependency(ofile, include);
6177             }
6178         }
6180       
6181     return true;
6186 /**
6187  *  High-level call to generate deps and optionally save them
6188  */
6189 bool DepTool::generateDependencies(const String &fileName)
6191     if (!createFileList())
6192         return false;
6193     if (!generateDependencies())
6194         return false;
6195     if (!saveDepFile(fileName))
6196         return false;
6197     return true;
6201 /**
6202  *   This saves the dependency cache.
6203  */
6204 bool DepTool::saveDepFile(const String &fileName)
6206     time_t tim;
6207     time(&tim);
6209     FILE *f = fopen(fileName.c_str(), "w");
6210     if (!f)
6211         {
6212         trace("cannot open '%s' for writing", fileName.c_str());
6213         }
6214     fprintf(f, "<?xml version='1.0'?>\n");
6215     fprintf(f, "<!--\n");
6216     fprintf(f, "########################################################\n");
6217     fprintf(f, "## File: build.dep\n");
6218     fprintf(f, "## Generated by BuildTool at :%s", ctime(&tim));
6219     fprintf(f, "########################################################\n");
6220     fprintf(f, "-->\n");
6222     fprintf(f, "<dependencies source='%s'>\n\n", sourceDir.c_str());
6223     std::map<String, FileRec *>::iterator iter;
6224     for (iter=oFiles.begin() ; iter!=oFiles.end() ; iter++)
6225         {
6226         FileRec *frec = iter->second;
6227         if (frec->type == FileRec::OFILE)
6228             {
6229             fprintf(f, "<object path='%s' name='%s' suffix='%s'>\n",
6230                  frec->path.c_str(), frec->baseName.c_str(), frec->suffix.c_str());
6231             std::map<String, FileRec *>::iterator citer;
6232             for (citer=frec->files.begin() ; citer!=frec->files.end() ; citer++)
6233                 {
6234                 String cfname = citer->first;
6235                 fprintf(f, "    <dep name='%s'/>\n", cfname.c_str());
6236                 }
6237             fprintf(f, "</object>\n\n");
6238             }
6239         }
6241     fprintf(f, "</dependencies>\n");
6242     fprintf(f, "\n");
6243     fprintf(f, "<!--\n");
6244     fprintf(f, "########################################################\n");
6245     fprintf(f, "## E N D\n");
6246     fprintf(f, "########################################################\n");
6247     fprintf(f, "-->\n");
6249     fclose(f);
6251     return true;
6257 /**
6258  *   This loads the dependency cache.
6259  */
6260 std::vector<DepRec> DepTool::loadDepFile(const String &depFile)
6262     std::vector<DepRec> result;
6263     
6264     Parser parser;
6265     Element *root = parser.parseFile(depFile.c_str());
6266     if (!root)
6267         {
6268         //error("Could not open %s for reading", depFile.c_str());
6269         return result;
6270         }
6272     if (root->getChildren().size()==0 ||
6273         root->getChildren()[0]->getName()!="dependencies")
6274         {
6275         error("loadDepFile: main xml element should be <dependencies>");
6276         delete root;
6277         return result;
6278         }
6280     //########## Start parsing
6281     Element *depList = root->getChildren()[0];
6283     std::vector<Element *> objects = depList->getChildren();
6284     for (unsigned int i=0 ; i<objects.size() ; i++)
6285         {
6286         Element *objectElem = objects[i];
6287         String tagName = objectElem->getName();
6288         if (tagName != "object")
6289             {
6290             error("loadDepFile: <dependencies> should have only <object> children");
6291             return result;
6292             }
6294         String objName   = objectElem->getAttribute("name");
6295          //trace("object:%s", objName.c_str());
6296         DepRec depObject(objName);
6297         depObject.path   = objectElem->getAttribute("path");
6298         depObject.suffix = objectElem->getAttribute("suffix");
6299         //########## DESCRIPTION
6300         std::vector<Element *> depElems = objectElem->getChildren();
6301         for (unsigned int i=0 ; i<depElems.size() ; i++)
6302             {
6303             Element *depElem = depElems[i];
6304             tagName = depElem->getName();
6305             if (tagName != "dep")
6306                 {
6307                 error("loadDepFile: <object> should have only <dep> children");
6308                 return result;
6309                 }
6310             String depName = depElem->getAttribute("name");
6311             //trace("    dep:%s", depName.c_str());
6312             depObject.files.push_back(depName);
6313             }
6315         //Insert into the result list, in a sorted manner
6316         bool inserted = false;
6317         std::vector<DepRec>::iterator iter;
6318         for (iter = result.begin() ; iter != result.end() ; iter++)
6319             {
6320             String vpath = iter->path;
6321             vpath.append("/");
6322             vpath.append(iter->name);
6323             String opath = depObject.path;
6324             opath.append("/");
6325             opath.append(depObject.name);
6326             if (vpath > opath)
6327                 {
6328                 inserted = true;
6329                 iter = result.insert(iter, depObject);
6330                 break;
6331                 }
6332             }
6333         if (!inserted)
6334             result.push_back(depObject);
6335         }
6337     delete root;
6339     return result;
6343 /**
6344  *   This loads the dependency cache.
6345  */
6346 std::vector<DepRec> DepTool::getDepFile(const String &depFile,
6347                    bool forceRefresh)
6349     std::vector<DepRec> result;
6350     if (forceRefresh)
6351         {
6352         generateDependencies(depFile);
6353         result = loadDepFile(depFile);
6354         }
6355     else
6356         {
6357         //try once
6358         result = loadDepFile(depFile);
6359         if (result.size() == 0)
6360             {
6361             //fail? try again
6362             generateDependencies(depFile);
6363             result = loadDepFile(depFile);
6364             }
6365         }
6366     return result;
6372 //########################################################################
6373 //# T A S K
6374 //########################################################################
6375 //forward decl
6376 class Target;
6377 class Make;
6379 /**
6380  *
6381  */
6382 class Task : public MakeBase
6385 public:
6387     typedef enum
6388         {
6389         TASK_NONE,
6390         TASK_CC,
6391         TASK_COPY,
6392         TASK_CXXTEST_PART,
6393         TASK_CXXTEST_ROOT,
6394         TASK_DELETE,
6395         TASK_ECHO,
6396         TASK_JAR,
6397         TASK_JAVAC,
6398         TASK_LINK,
6399         TASK_MAKEFILE,
6400         TASK_MKDIR,
6401         TASK_MSGFMT,
6402         TASK_PKG_CONFIG,
6403         TASK_RANLIB,
6404         TASK_RC,
6405         TASK_SHAREDLIB,
6406         TASK_STATICLIB,
6407         TASK_STRIP,
6408         TASK_TOUCH,
6409         TASK_TSTAMP
6410         } TaskType;
6411         
6413     /**
6414      *
6415      */
6416     Task(MakeBase &par) : parent(par)
6417         { init(); }
6419     /**
6420      *
6421      */
6422     Task(const Task &other) : parent(other.parent)
6423         { init(); assign(other); }
6425     /**
6426      *
6427      */
6428     Task &operator=(const Task &other)
6429         { assign(other); return *this; }
6431     /**
6432      *
6433      */
6434     virtual ~Task()
6435         { }
6438     /**
6439      *
6440      */
6441     virtual MakeBase &getParent()
6442         { return parent; }
6444      /**
6445      *
6446      */
6447     virtual int  getType()
6448         { return type; }
6450     /**
6451      *
6452      */
6453     virtual void setType(int val)
6454         { type = val; }
6456     /**
6457      *
6458      */
6459     virtual String getName()
6460         { return name; }
6462     /**
6463      *
6464      */
6465     virtual bool execute()
6466         { return true; }
6468     /**
6469      *
6470      */
6471     virtual bool parse(Element *elem)
6472         { return true; }
6474     /**
6475      *
6476      */
6477     Task *createTask(Element *elem, int lineNr);
6480 protected:
6482     void init()
6483         {
6484         type = TASK_NONE;
6485         name = "none";
6486         }
6488     void assign(const Task &other)
6489         {
6490         type = other.type;
6491         name = other.name;
6492         }
6493         
6494     /**
6495      *  Show task status
6496      */
6497     void taskstatus(const char *fmt, ...)
6498         {
6499         va_list args;
6500         va_start(args,fmt);
6501         fprintf(stdout, "    %s : ", name.c_str());
6502         vfprintf(stdout, fmt, args);
6503         fprintf(stdout, "\n");
6504         va_end(args) ;
6505         }
6507     String getAttribute(Element *elem, const String &attrName)
6508         {
6509         String str;
6510         return str;
6511         }
6513     MakeBase &parent;
6515     int type;
6517     String name;
6518 };
6522 /**
6523  * This task runs the C/C++ compiler.  The compiler is invoked
6524  * for all .c or .cpp files which are newer than their correcsponding
6525  * .o files.  
6526  */
6527 class TaskCC : public Task
6529 public:
6531     TaskCC(MakeBase &par) : Task(par)
6532         {
6533         type = TASK_CC;
6534         name = "cc";
6535         }
6537     virtual ~TaskCC()
6538         {}
6539         
6540     virtual bool isExcludedInc(const String &dirname)
6541         {
6542         for (unsigned int i=0 ; i<excludeInc.size() ; i++)
6543             {
6544             String fname = excludeInc[i];
6545             if (fname == dirname)
6546                 return true;
6547             }
6548         return false;
6549         }
6551     virtual bool execute()
6552         {
6553         //evaluate our parameters
6554         String command         = parent.eval(commandOpt, "gcc");
6555         String ccCommand       = parent.eval(ccCommandOpt, "gcc");
6556         String cxxCommand      = parent.eval(cxxCommandOpt, "g++");
6557         String source          = parent.eval(sourceOpt, ".");
6558         String dest            = parent.eval(destOpt, ".");
6559         String flags           = parent.eval(flagsOpt, "");
6560         String defines         = parent.eval(definesOpt, "");
6561         String includes        = parent.eval(includesOpt, "");
6562         bool continueOnError   = parent.evalBool(continueOnErrorOpt, true);
6563         bool refreshCache      = parent.evalBool(refreshCacheOpt, false);
6565         if (!listFiles(parent, fileSet))
6566             return false;
6567             
6568         FILE *f = NULL;
6569         f = fopen("compile.lst", "w");
6571         //refreshCache is probably false here, unless specified otherwise
6572         String fullName = parent.resolve("build.dep");
6573         if (refreshCache || isNewerThan(parent.getURI().getPath(), fullName))
6574             {
6575             taskstatus("regenerating C/C++ dependency cache");
6576             refreshCache = true;
6577             }
6579         DepTool depTool;
6580         depTool.setSourceDirectory(source);
6581         depTool.setFileList(fileSet.getFiles());
6582         std::vector<DepRec> deps =
6583              depTool.getDepFile("build.dep", refreshCache);
6584         
6585         String incs;
6586         incs.append("-I");
6587         incs.append(parent.resolve("."));
6588         incs.append(" ");
6589         if (includes.size()>0)
6590             {
6591             incs.append(includes);
6592             incs.append(" ");
6593             }
6594         std::set<String> paths;
6595         std::vector<DepRec>::iterator viter;
6596         for (viter=deps.begin() ; viter!=deps.end() ; viter++)
6597             {
6598             DepRec dep = *viter;
6599             if (dep.path.size()>0)
6600                 paths.insert(dep.path);
6601             }
6602         if (source.size()>0)
6603             {
6604             incs.append(" -I");
6605             incs.append(parent.resolve(source));
6606             incs.append(" ");
6607             }
6608         std::set<String>::iterator setIter;
6609         for (setIter=paths.begin() ; setIter!=paths.end() ; setIter++)
6610             {
6611             String dirName = *setIter;
6612             //check excludeInc to see if we dont want to include this dir
6613             if (isExcludedInc(dirName))
6614                 continue;
6615             incs.append(" -I");
6616             String dname;
6617             if (source.size()>0)
6618                 {
6619                 dname.append(source);
6620                 dname.append("/");
6621                 }
6622             dname.append(dirName);
6623             incs.append(parent.resolve(dname));
6624             }
6625             
6626         /**
6627          * Compile each of the C files that need it
6628          */
6629         bool errorOccurred = false;                 
6630         std::vector<String> cfiles;
6631         for (viter=deps.begin() ; viter!=deps.end() ; viter++)
6632             {
6633             DepRec dep = *viter;
6635             //## Select command
6636             String sfx = dep.suffix;
6637             String command = ccCommand;
6638             if (sfx == "cpp" || sfx == "cxx" || sfx == "c++" ||
6639                  sfx == "cc" || sfx == "CC")
6640                 command = cxxCommand;
6641  
6642             //## Make paths
6643             String destPath = dest;
6644             String srcPath  = source;
6645             if (dep.path.size()>0)
6646                 {
6647                 destPath.append("/");
6648                 destPath.append(dep.path);
6649                 srcPath.append("/");
6650                 srcPath.append(dep.path);
6651                 }
6652             //## Make sure destination directory exists
6653             if (!createDirectory(destPath))
6654                 return false;
6655                 
6656             //## Check whether it needs to be done
6657             String destName;
6658             if (destPath.size()>0)
6659                 {
6660                 destName.append(destPath);
6661                 destName.append("/");
6662                 }
6663             destName.append(dep.name);
6664             destName.append(".o");
6665             String destFullName = parent.resolve(destName);
6666             String srcName;
6667             if (srcPath.size()>0)
6668                 {
6669                 srcName.append(srcPath);
6670                 srcName.append("/");
6671                 }
6672             srcName.append(dep.name);
6673             srcName.append(".");
6674             srcName.append(dep.suffix);
6675             String srcFullName = parent.resolve(srcName);
6676             bool compileMe = false;
6677             //# First we check if the source is newer than the .o
6678             if (isNewerThan(srcFullName, destFullName))
6679                 {
6680                 taskstatus("compile of %s required by source: %s",
6681                         destFullName.c_str(), srcFullName.c_str());
6682                 compileMe = true;
6683                 }
6684             else
6685                 {
6686                 //# secondly, we check if any of the included dependencies
6687                 //# of the .c/.cpp is newer than the .o
6688                 for (unsigned int i=0 ; i<dep.files.size() ; i++)
6689                     {
6690                     String depName;
6691                     if (source.size()>0)
6692                         {
6693                         depName.append(source);
6694                         depName.append("/");
6695                         }
6696                     depName.append(dep.files[i]);
6697                     String depFullName = parent.resolve(depName);
6698                     bool depRequires = isNewerThan(depFullName, destFullName);
6699                     //trace("%d %s %s\n", depRequires,
6700                     //        destFullName.c_str(), depFullName.c_str());
6701                     if (depRequires)
6702                         {
6703                         taskstatus("compile of %s required by included: %s",
6704                                 destFullName.c_str(), depFullName.c_str());
6705                         compileMe = true;
6706                         break;
6707                         }
6708                     }
6709                 }
6710             if (!compileMe)
6711                 {
6712                 continue;
6713                 }
6715             //## Assemble the command
6716             String cmd = command;
6717             cmd.append(" -c ");
6718             cmd.append(flags);
6719             cmd.append(" ");
6720             cmd.append(defines);
6721             cmd.append(" ");
6722             cmd.append(incs);
6723             cmd.append(" ");
6724             cmd.append(srcFullName);
6725             cmd.append(" -o ");
6726             cmd.append(destFullName);
6728             //## Execute the command
6730             String outString, errString;
6731             bool ret = executeCommand(cmd.c_str(), "", outString, errString);
6733             if (f)
6734                 {
6735                 fprintf(f, "########################### File : %s\n",
6736                              srcFullName.c_str());
6737                 fprintf(f, "#### COMMAND ###\n");
6738                 int col = 0;
6739                 for (unsigned int i = 0 ; i < cmd.size() ; i++)
6740                     {
6741                     char ch = cmd[i];
6742                     if (isspace(ch)  && col > 63)
6743                         {
6744                         fputc('\n', f);
6745                         col = 0;
6746                         }
6747                     else
6748                         {
6749                         fputc(ch, f);
6750                         col++;
6751                         }
6752                     if (col > 76)
6753                         {
6754                         fputc('\n', f);
6755                         col = 0;
6756                         }
6757                     }
6758                 fprintf(f, "\n");
6759                 fprintf(f, "#### STDOUT ###\n%s\n", outString.c_str());
6760                 fprintf(f, "#### STDERR ###\n%s\n\n", errString.c_str());
6761                 fflush(f);
6762                 }
6763             if (!ret)
6764                 {
6765                 error("problem compiling: %s", errString.c_str());
6766                 errorOccurred = true;
6767                 }
6768             if (errorOccurred && !continueOnError)
6769                 break;
6770             }
6772         if (f)
6773             {
6774             fclose(f);
6775             }
6776         
6777         return !errorOccurred;
6778         }
6781     virtual bool parse(Element *elem)
6782         {
6783         String s;
6784         if (!parent.getAttribute(elem, "command", commandOpt))
6785             return false;
6786         if (commandOpt.size()>0)
6787             { cxxCommandOpt = ccCommandOpt = commandOpt; }
6788         if (!parent.getAttribute(elem, "cc", ccCommandOpt))
6789             return false;
6790         if (!parent.getAttribute(elem, "cxx", cxxCommandOpt))
6791             return false;
6792         if (!parent.getAttribute(elem, "destdir", destOpt))
6793             return false;
6794         if (!parent.getAttribute(elem, "continueOnError", continueOnErrorOpt))
6795             return false;
6796         if (!parent.getAttribute(elem, "refreshCache", refreshCacheOpt))
6797             return false;
6799         std::vector<Element *> children = elem->getChildren();
6800         for (unsigned int i=0 ; i<children.size() ; i++)
6801             {
6802             Element *child = children[i];
6803             String tagName = child->getName();
6804             if (tagName == "flags")
6805                 {
6806                 if (!parent.getValue(child, flagsOpt))
6807                     return false;
6808                 flagsOpt = strip(flagsOpt);
6809                 }
6810             else if (tagName == "includes")
6811                 {
6812                 if (!parent.getValue(child, includesOpt))
6813                     return false;
6814                 includesOpt = strip(includesOpt);
6815                 }
6816             else if (tagName == "defines")
6817                 {
6818                 if (!parent.getValue(child, definesOpt))
6819                     return false;
6820                 definesOpt = strip(definesOpt);
6821                 }
6822             else if (tagName == "fileset")
6823                 {
6824                 if (!parseFileSet(child, parent, fileSet))
6825                     return false;
6826                 sourceOpt = fileSet.getDirectory();
6827                 }
6828             else if (tagName == "excludeinc")
6829                 {
6830                 if (!parseFileList(child, parent, excludeInc))
6831                     return false;
6832                 }
6833             }
6835         return true;
6836         }
6837         
6838 protected:
6840     String   commandOpt;
6841     String   ccCommandOpt;
6842     String   cxxCommandOpt;
6843     String   sourceOpt;
6844     String   destOpt;
6845     String   flagsOpt;
6846     String   definesOpt;
6847     String   includesOpt;
6848     String   continueOnErrorOpt;
6849     String   refreshCacheOpt;
6850     FileSet  fileSet;
6851     FileList excludeInc;
6852     
6853 };
6857 /**
6858  *
6859  */
6860 class TaskCopy : public Task
6862 public:
6864     typedef enum
6865         {
6866         CP_NONE,
6867         CP_TOFILE,
6868         CP_TODIR
6869         } CopyType;
6871     TaskCopy(MakeBase &par) : Task(par)
6872         {
6873         type        = TASK_COPY;
6874         name        = "copy";
6875         cptype      = CP_NONE;
6876         haveFileSet = false;
6877         }
6879     virtual ~TaskCopy()
6880         {}
6882     virtual bool execute()
6883         {
6884         String fileName   = parent.eval(fileNameOpt   , ".");
6885         String toFileName = parent.eval(toFileNameOpt , ".");
6886         String toDirName  = parent.eval(toDirNameOpt  , ".");
6887         bool   verbose    = parent.evalBool(verboseOpt, false);
6888         switch (cptype)
6889            {
6890            case CP_TOFILE:
6891                {
6892                if (fileName.size()>0)
6893                    {
6894                    taskstatus("%s to %s",
6895                         fileName.c_str(), toFileName.c_str());
6896                    String fullSource = parent.resolve(fileName);
6897                    String fullDest = parent.resolve(toFileName);
6898                    if (verbose)
6899                        taskstatus("copy %s to file %s", fullSource.c_str(),
6900                                           fullDest.c_str());
6901                    if (!isRegularFile(fullSource))
6902                        {
6903                        error("copy : file %s does not exist", fullSource.c_str());
6904                        return false;
6905                        }
6906                    if (!isNewerThan(fullSource, fullDest))
6907                        {
6908                        taskstatus("skipped");
6909                        return true;
6910                        }
6911                    if (!copyFile(fullSource, fullDest))
6912                        return false;
6913                    taskstatus("1 file copied");
6914                    }
6915                return true;
6916                }
6917            case CP_TODIR:
6918                {
6919                if (haveFileSet)
6920                    {
6921                    if (!listFiles(parent, fileSet))
6922                        return false;
6923                    String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
6925                    taskstatus("%s to %s",
6926                        fileSetDir.c_str(), toDirName.c_str());
6928                    int nrFiles = 0;
6929                    for (unsigned int i=0 ; i<fileSet.size() ; i++)
6930                        {
6931                        String fileName = fileSet[i];
6933                        String sourcePath;
6934                        if (fileSetDir.size()>0)
6935                            {
6936                            sourcePath.append(fileSetDir);
6937                            sourcePath.append("/");
6938                            }
6939                        sourcePath.append(fileName);
6940                        String fullSource = parent.resolve(sourcePath);
6941                        
6942                        //Get the immediate parent directory's base name
6943                        String baseFileSetDir = fileSetDir;
6944                        unsigned int pos = baseFileSetDir.find_last_of('/');
6945                        if (pos!=baseFileSetDir.npos &&
6946                                   pos < baseFileSetDir.size()-1)
6947                            baseFileSetDir =
6948                               baseFileSetDir.substr(pos+1,
6949                                    baseFileSetDir.size());
6950                        //Now make the new path
6951                        String destPath;
6952                        if (toDirName.size()>0)
6953                            {
6954                            destPath.append(toDirName);
6955                            destPath.append("/");
6956                            }
6957                        if (baseFileSetDir.size()>0)
6958                            {
6959                            destPath.append(baseFileSetDir);
6960                            destPath.append("/");
6961                            }
6962                        destPath.append(fileName);
6963                        String fullDest = parent.resolve(destPath);
6964                        //trace("fileName:%s", fileName.c_str());
6965                        if (verbose)
6966                            taskstatus("copy %s to new dir : %s",
6967                                  fullSource.c_str(), fullDest.c_str());
6968                        if (!isNewerThan(fullSource, fullDest))
6969                            {
6970                            if (verbose)
6971                                taskstatus("copy skipping %s", fullSource.c_str());
6972                            continue;
6973                            }
6974                        if (!copyFile(fullSource, fullDest))
6975                            return false;
6976                        nrFiles++;
6977                        }
6978                    taskstatus("%d file(s) copied", nrFiles);
6979                    }
6980                else //file source
6981                    {
6982                    //For file->dir we want only the basename of
6983                    //the source appended to the dest dir
6984                    taskstatus("%s to %s", 
6985                        fileName.c_str(), toDirName.c_str());
6986                    String baseName = fileName;
6987                    unsigned int pos = baseName.find_last_of('/');
6988                    if (pos!=baseName.npos && pos<baseName.size()-1)
6989                        baseName = baseName.substr(pos+1, baseName.size());
6990                    String fullSource = parent.resolve(fileName);
6991                    String destPath;
6992                    if (toDirName.size()>0)
6993                        {
6994                        destPath.append(toDirName);
6995                        destPath.append("/");
6996                        }
6997                    destPath.append(baseName);
6998                    String fullDest = parent.resolve(destPath);
6999                    if (verbose)
7000                        taskstatus("file %s to new dir : %s", fullSource.c_str(),
7001                                           fullDest.c_str());
7002                    if (!isRegularFile(fullSource))
7003                        {
7004                        error("copy : file %s does not exist", fullSource.c_str());
7005                        return false;
7006                        }
7007                    if (!isNewerThan(fullSource, fullDest))
7008                        {
7009                        taskstatus("skipped");
7010                        return true;
7011                        }
7012                    if (!copyFile(fullSource, fullDest))
7013                        return false;
7014                    taskstatus("1 file copied");
7015                    }
7016                return true;
7017                }
7018            }
7019         return true;
7020         }
7023     virtual bool parse(Element *elem)
7024         {
7025         if (!parent.getAttribute(elem, "file", fileNameOpt))
7026             return false;
7027         if (!parent.getAttribute(elem, "tofile", toFileNameOpt))
7028             return false;
7029         if (toFileNameOpt.size() > 0)
7030             cptype = CP_TOFILE;
7031         if (!parent.getAttribute(elem, "todir", toDirNameOpt))
7032             return false;
7033         if (toDirNameOpt.size() > 0)
7034             cptype = CP_TODIR;
7035         if (!parent.getAttribute(elem, "verbose", verboseOpt))
7036             return false;
7037             
7038         haveFileSet = false;
7039         
7040         std::vector<Element *> children = elem->getChildren();
7041         for (unsigned int i=0 ; i<children.size() ; i++)
7042             {
7043             Element *child = children[i];
7044             String tagName = child->getName();
7045             if (tagName == "fileset")
7046                 {
7047                 if (!parseFileSet(child, parent, fileSet))
7048                     {
7049                     error("problem getting fileset");
7050                     return false;
7051                     }
7052                 haveFileSet = true;
7053                 }
7054             }
7056         //Perform validity checks
7057         if (fileNameOpt.size()>0 && fileSet.size()>0)
7058             {
7059             error("<copy> can only have one of : file= and <fileset>");
7060             return false;
7061             }
7062         if (toFileNameOpt.size()>0 && toDirNameOpt.size()>0)
7063             {
7064             error("<copy> can only have one of : tofile= or todir=");
7065             return false;
7066             }
7067         if (haveFileSet && toDirNameOpt.size()==0)
7068             {
7069             error("a <copy> task with a <fileset> must have : todir=");
7070             return false;
7071             }
7072         if (cptype == CP_TOFILE && fileNameOpt.size()==0)
7073             {
7074             error("<copy> tofile= must be associated with : file=");
7075             return false;
7076             }
7077         if (cptype == CP_TODIR && fileNameOpt.size()==0 && !haveFileSet)
7078             {
7079             error("<copy> todir= must be associated with : file= or <fileset>");
7080             return false;
7081             }
7083         return true;
7084         }
7085         
7086 private:
7088     int cptype;
7089     bool haveFileSet;
7091     FileSet fileSet;
7092     String  fileNameOpt;
7093     String  toFileNameOpt;
7094     String  toDirNameOpt;
7095     String  verboseOpt;
7096 };
7099 /**
7100  * Generate CxxTest files
7101  */
7102 class TaskCxxTestPart: public Task
7104 public:
7106     TaskCxxTestPart(MakeBase &par) : Task(par)
7107          {
7108          type    = TASK_CXXTEST_PART;
7109          name    = "cxxtestpart";
7110          }
7112     virtual ~TaskCxxTestPart()
7113         {}
7115     virtual bool execute()
7116         {
7117         if (!listFiles(parent, fileSet))
7118             return false;
7119         String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
7120                 
7121         String fullDest = parent.resolve(parent.eval(destPathOpt, "."));
7122         String cmd = parent.eval(commandOpt, "cxxtestgen.py");
7123         cmd.append(" --part -o ");
7124         cmd.append(fullDest);
7126         unsigned int newFiles = 0;
7127         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7128             {
7129             String fileName = fileSet[i];
7130             if (getSuffix(fileName) != "h")
7131                 continue;
7132             String sourcePath;
7133             if (fileSetDir.size()>0)
7134                 {
7135                 sourcePath.append(fileSetDir);
7136                 sourcePath.append("/");
7137                 }
7138             sourcePath.append(fileName);
7139             String fullSource = parent.resolve(sourcePath);
7141             cmd.append(" ");
7142             cmd.append(fullSource);
7143             if (isNewerThan(fullSource, fullDest)) newFiles++;
7144             }
7145         
7146         if (newFiles>0) {
7147             size_t const lastSlash = fullDest.find_last_of('/');
7148             if (lastSlash != fullDest.npos) {
7149                 String directory(fullDest, 0, lastSlash);
7150                 if (!createDirectory(directory))
7151                     return false;
7152             }
7154             String outString, errString;
7155             if (!executeCommand(cmd.c_str(), "", outString, errString))
7156                 {
7157                 error("<cxxtestpart> problem: %s", errString.c_str());
7158                 return false;
7159                 }
7160         }
7162         return true;
7163         }
7165     virtual bool parse(Element *elem)
7166         {
7167         if (!parent.getAttribute(elem, "command", commandOpt))
7168             return false;
7169         if (!parent.getAttribute(elem, "out", destPathOpt))
7170             return false;
7171             
7172         std::vector<Element *> children = elem->getChildren();
7173         for (unsigned int i=0 ; i<children.size() ; i++)
7174             {
7175             Element *child = children[i];
7176             String tagName = child->getName();
7177             if (tagName == "fileset")
7178                 {
7179                 if (!parseFileSet(child, parent, fileSet))
7180                     return false;
7181                 }
7182             }
7183         return true;
7184         }
7186 private:
7188     String  commandOpt;
7189     String  destPathOpt;
7190     FileSet fileSet;
7192 };
7195 /**
7196  * Generate the CxxTest root file
7197  */
7198 class TaskCxxTestRoot: public Task
7200 public:
7202     TaskCxxTestRoot(MakeBase &par) : Task(par)
7203          {
7204          type    = TASK_CXXTEST_ROOT;
7205          name    = "cxxtestroot";
7206          }
7208     virtual ~TaskCxxTestRoot()
7209         {}
7211     virtual bool execute()
7212         {
7213         if (!listFiles(parent, fileSet))
7214             return false;
7215         String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
7216         unsigned int newFiles = 0;
7217                 
7218         String fullDest = parent.resolve(parent.eval(destPathOpt, "."));
7219         String cmd = parent.eval(commandOpt, "cxxtestgen.py");
7220         cmd.append(" --root -o ");
7221         cmd.append(fullDest);
7222         String templateFile = parent.eval(templateFileOpt, "");
7223         if (templateFile.size()>0) {
7224             String fullTemplate = parent.resolve(templateFile);
7225             cmd.append(" --template=");
7226             cmd.append(fullTemplate);
7227             if (isNewerThan(fullTemplate, fullDest)) newFiles++;
7228         }
7230         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7231             {
7232             String fileName = fileSet[i];
7233             if (getSuffix(fileName) != "h")
7234                 continue;
7235             String sourcePath;
7236             if (fileSetDir.size()>0)
7237                 {
7238                 sourcePath.append(fileSetDir);
7239                 sourcePath.append("/");
7240                 }
7241             sourcePath.append(fileName);
7242             String fullSource = parent.resolve(sourcePath);
7244             cmd.append(" ");
7245             cmd.append(fullSource);
7246             if (isNewerThan(fullSource, fullDest)) newFiles++;
7247             }
7248         
7249         if (newFiles>0) {
7250             size_t const lastSlash = fullDest.find_last_of('/');
7251             if (lastSlash != fullDest.npos) {
7252                 String directory(fullDest, 0, lastSlash);
7253                 if (!createDirectory(directory))
7254                     return false;
7255             }
7257             String outString, errString;
7258             if (!executeCommand(cmd.c_str(), "", outString, errString))
7259                 {
7260                 error("<cxxtestroot> problem: %s", errString.c_str());
7261                 return false;
7262                 }
7263         }
7265         return true;
7266         }
7268     virtual bool parse(Element *elem)
7269         {
7270         if (!parent.getAttribute(elem, "command", commandOpt))
7271             return false;
7272         if (!parent.getAttribute(elem, "template", templateFileOpt))
7273             return false;
7274         if (!parent.getAttribute(elem, "out", destPathOpt))
7275             return false;
7276             
7277         std::vector<Element *> children = elem->getChildren();
7278         for (unsigned int i=0 ; i<children.size() ; i++)
7279             {
7280             Element *child = children[i];
7281             String tagName = child->getName();
7282             if (tagName == "fileset")
7283                 {
7284                 if (!parseFileSet(child, parent, fileSet))
7285                     return false;
7286                 }
7287             }
7288         return true;
7289         }
7291 private:
7293     String  commandOpt;
7294     String  templateFileOpt;
7295     String  destPathOpt;
7296     FileSet fileSet;
7298 };
7301 /**
7302  *
7303  */
7304 class TaskDelete : public Task
7306 public:
7308     typedef enum
7309         {
7310         DEL_FILE,
7311         DEL_DIR,
7312         DEL_FILESET
7313         } DeleteType;
7315     TaskDelete(MakeBase &par) : Task(par)
7316         { 
7317         type        = TASK_DELETE;
7318         name        = "delete";
7319         delType     = DEL_FILE;
7320         }
7322     virtual ~TaskDelete()
7323         {}
7325     virtual bool execute()
7326         {
7327         String dirName   = parent.eval(dirNameOpt, ".");
7328         String fileName  = parent.eval(fileNameOpt, ".");
7329         bool verbose     = parent.evalBool(verboseOpt, false);
7330         bool quiet       = parent.evalBool(quietOpt, false);
7331         bool failOnError = parent.evalBool(failOnErrorOpt, true);
7332         struct stat finfo;
7333         switch (delType)
7334             {
7335             case DEL_FILE:
7336                 {
7337                 taskstatus("file: %s", fileName.c_str());
7338                 String fullName = parent.resolve(fileName);
7339                 char *fname = (char *)fullName.c_str();
7340                 if (!quiet && verbose)
7341                     taskstatus("path: %s", fname);
7342                 //does not exist
7343                 if (cachedStat(fullName, &finfo)<0)
7344                     {
7345                     if (failOnError)
7346                         return false;
7347                     else
7348                         return true;
7349                     }
7350                 //exists but is not a regular file
7351                 if (!S_ISREG(finfo.st_mode))
7352                     {
7353                     error("<delete> failed. '%s' exists and is not a regular file",
7354                           fname);
7355                     return false;
7356                     }
7357                 if (remove(fname)<0)
7358                     {
7359                     error("<delete> failed: %s", strerror(errno));
7360                     return false;
7361                     }
7362                 return true;
7363                 }
7364             case DEL_DIR:
7365                 {
7366                 taskstatus("dir: %s", dirName.c_str());
7367                 String fullDir = parent.resolve(dirName);
7368                 if (!quiet && verbose)
7369                     taskstatus("path: %s", fullDir.c_str());
7370                 if (!removeDirectory(fullDir))
7371                     return false;
7372                 return true;
7373                 }
7374             }
7375         return true;
7376         }
7378     virtual bool parse(Element *elem)
7379         {
7380         if (!parent.getAttribute(elem, "file", fileNameOpt))
7381             return false;
7382         if (fileNameOpt.size() > 0)
7383             delType = DEL_FILE;
7384         if (!parent.getAttribute(elem, "dir", dirNameOpt))
7385             return false;
7386         if (dirNameOpt.size() > 0)
7387             delType = DEL_DIR;
7388         if (fileNameOpt.size()>0 && dirNameOpt.size()>0)
7389             {
7390             error("<delete> can have one attribute of file= or dir=");
7391             return false;
7392             }
7393         if (fileNameOpt.size()==0 && dirNameOpt.size()==0)
7394             {
7395             error("<delete> must have one attribute of file= or dir=");
7396             return false;
7397             }
7398         if (!parent.getAttribute(elem, "verbose", verboseOpt))
7399             return false;
7400         if (!parent.getAttribute(elem, "quiet", quietOpt))
7401             return false;
7402         if (!parent.getAttribute(elem, "failonerror", failOnErrorOpt))
7403             return false;
7404         return true;
7405         }
7407 private:
7409     int delType;
7410     String dirNameOpt;
7411     String fileNameOpt;
7412     String verboseOpt;
7413     String quietOpt;
7414     String failOnErrorOpt;
7415 };
7418 /**
7419  * Send a message to stdout
7420  */
7421 class TaskEcho : public Task
7423 public:
7425     TaskEcho(MakeBase &par) : Task(par)
7426         { type = TASK_ECHO; name = "echo"; }
7428     virtual ~TaskEcho()
7429         {}
7431     virtual bool execute()
7432         {
7433         //let message have priority over text
7434         String message = parent.eval(messageOpt, "");
7435         String text    = parent.eval(textOpt, "");
7436         if (message.size() > 0)
7437             {
7438             fprintf(stdout, "%s\n", message.c_str());
7439             }
7440         else if (text.size() > 0)
7441             {
7442             fprintf(stdout, "%s\n", text.c_str());
7443             }
7444         return true;
7445         }
7447     virtual bool parse(Element *elem)
7448         {
7449         if (!parent.getValue(elem, textOpt))
7450             return false;
7451         textOpt    = leftJustify(textOpt);
7452         if (!parent.getAttribute(elem, "message", messageOpt))
7453             return false;
7454         return true;
7455         }
7457 private:
7459     String messageOpt;
7460     String textOpt;
7461 };
7465 /**
7466  *
7467  */
7468 class TaskJar : public Task
7470 public:
7472     TaskJar(MakeBase &par) : Task(par)
7473         { type = TASK_JAR; name = "jar"; }
7475     virtual ~TaskJar()
7476         {}
7478     virtual bool execute()
7479         {
7480         String command  = parent.eval(commandOpt, "jar");
7481         String basedir  = parent.eval(basedirOpt, ".");
7482         String destfile = parent.eval(destfileOpt, ".");
7484         String cmd = command;
7485         cmd.append(" -cf ");
7486         cmd.append(destfile);
7487         cmd.append(" -C ");
7488         cmd.append(basedir);
7489         cmd.append(" .");
7491         String execCmd = cmd;
7493         String outString, errString;
7494         bool ret = executeCommand(execCmd.c_str(), "", outString, errString);
7495         if (!ret)
7496             {
7497             error("<jar> command '%s' failed :\n %s",
7498                                       execCmd.c_str(), errString.c_str());
7499             return false;
7500             }
7501         return true;
7502         }
7504     virtual bool parse(Element *elem)
7505         {
7506         if (!parent.getAttribute(elem, "command", commandOpt))
7507             return false;
7508         if (!parent.getAttribute(elem, "basedir", basedirOpt))
7509             return false;
7510         if (!parent.getAttribute(elem, "destfile", destfileOpt))
7511             return false;
7512         if (basedirOpt.size() == 0 || destfileOpt.size() == 0)
7513             {
7514             error("<jar> required both basedir and destfile attributes to be set");
7515             return false;
7516             }
7517         return true;
7518         }
7520 private:
7522     String commandOpt;
7523     String basedirOpt;
7524     String destfileOpt;
7525 };
7528 /**
7529  *
7530  */
7531 class TaskJavac : public Task
7533 public:
7535     TaskJavac(MakeBase &par) : Task(par)
7536         { 
7537         type = TASK_JAVAC; name = "javac";
7538         }
7540     virtual ~TaskJavac()
7541         {}
7543     virtual bool execute()
7544         {
7545         String command  = parent.eval(commandOpt, "javac");
7546         String srcdir   = parent.eval(srcdirOpt, ".");
7547         String destdir  = parent.eval(destdirOpt, ".");
7548         String target   = parent.eval(targetOpt, "");
7550         std::vector<String> fileList;
7551         if (!listFiles(srcdir, "", fileList))
7552             {
7553             return false;
7554             }
7555         String cmd = command;
7556         cmd.append(" -d ");
7557         cmd.append(destdir);
7558         cmd.append(" -classpath ");
7559         cmd.append(destdir);
7560         cmd.append(" -sourcepath ");
7561         cmd.append(srcdir);
7562         cmd.append(" ");
7563         if (target.size()>0)
7564             {
7565             cmd.append(" -target ");
7566             cmd.append(target);
7567             cmd.append(" ");
7568             }
7569         String fname = "javalist.btool";
7570         FILE *f = fopen(fname.c_str(), "w");
7571         int count = 0;
7572         for (unsigned int i=0 ; i<fileList.size() ; i++)
7573             {
7574             String fname = fileList[i];
7575             String srcName = fname;
7576             if (fname.size()<6) //x.java
7577                 continue;
7578             if (fname.compare(fname.size()-5, 5, ".java") != 0)
7579                 continue;
7580             String baseName = fname.substr(0, fname.size()-5);
7581             String destName = baseName;
7582             destName.append(".class");
7584             String fullSrc = srcdir;
7585             fullSrc.append("/");
7586             fullSrc.append(fname);
7587             String fullDest = destdir;
7588             fullDest.append("/");
7589             fullDest.append(destName);
7590             //trace("fullsrc:%s fulldest:%s", fullSrc.c_str(), fullDest.c_str());
7591             if (!isNewerThan(fullSrc, fullDest))
7592                 continue;
7594             count++;
7595             fprintf(f, "%s\n", fullSrc.c_str());
7596             }
7597         fclose(f);
7598         if (!count)
7599             {
7600             taskstatus("nothing to do");
7601             return true;
7602             }
7604         taskstatus("compiling %d files", count);
7606         String execCmd = cmd;
7607         execCmd.append("@");
7608         execCmd.append(fname);
7610         String outString, errString;
7611         bool ret = executeCommand(execCmd.c_str(), "", outString, errString);
7612         if (!ret)
7613             {
7614             error("<javac> command '%s' failed :\n %s",
7615                                       execCmd.c_str(), errString.c_str());
7616             return false;
7617             }
7618         return true;
7619         }
7621     virtual bool parse(Element *elem)
7622         {
7623         if (!parent.getAttribute(elem, "command", commandOpt))
7624             return false;
7625         if (!parent.getAttribute(elem, "srcdir", srcdirOpt))
7626             return false;
7627         if (!parent.getAttribute(elem, "destdir", destdirOpt))
7628             return false;
7629         if (srcdirOpt.size() == 0 || destdirOpt.size() == 0)
7630             {
7631             error("<javac> required both srcdir and destdir attributes to be set");
7632             return false;
7633             }
7634         if (!parent.getAttribute(elem, "target", targetOpt))
7635             return false;
7636         return true;
7637         }
7639 private:
7641     String commandOpt;
7642     String srcdirOpt;
7643     String destdirOpt;
7644     String targetOpt;
7646 };
7649 /**
7650  *
7651  */
7652 class TaskLink : public Task
7654 public:
7656     TaskLink(MakeBase &par) : Task(par)
7657         {
7658         type = TASK_LINK; name = "link";
7659         }
7661     virtual ~TaskLink()
7662         {}
7664     virtual bool execute()
7665         {
7666         String  command        = parent.eval(commandOpt, "g++");
7667         String  fileName       = parent.eval(fileNameOpt, "");
7668         String  flags          = parent.eval(flagsOpt, "");
7669         String  libs           = parent.eval(libsOpt, "");
7670         bool    doStrip        = parent.evalBool(doStripOpt, false);
7671         String  symFileName    = parent.eval(symFileNameOpt, "");
7672         String  stripCommand   = parent.eval(stripCommandOpt, "strip");
7673         String  objcopyCommand = parent.eval(objcopyCommandOpt, "objcopy");
7675         if (!listFiles(parent, fileSet))
7676             return false;
7677         String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
7678         //trace("%d files in %s", fileSet.size(), fileSetDir.c_str());
7679         bool doit = false;
7680         String fullTarget = parent.resolve(fileName);
7681         String cmd = command;
7682         cmd.append(" -o ");
7683         cmd.append(fullTarget);
7684         cmd.append(" ");
7685         cmd.append(flags);
7686         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7687             {
7688             cmd.append(" ");
7689             String obj;
7690             if (fileSetDir.size()>0)
7691                 {
7692                 obj.append(fileSetDir);
7693                 obj.append("/");
7694                 }
7695             obj.append(fileSet[i]);
7696             String fullObj = parent.resolve(obj);
7697             String nativeFullObj = getNativePath(fullObj);
7698             cmd.append(nativeFullObj);
7699             //trace("link: tgt:%s obj:%s", fullTarget.c_str(),
7700             //          fullObj.c_str());
7701             if (isNewerThan(fullObj, fullTarget))
7702                 doit = true;
7703             }
7704         cmd.append(" ");
7705         cmd.append(libs);
7706         if (!doit)
7707             {
7708             //trace("link not needed");
7709             return true;
7710             }
7711         //trace("LINK cmd:%s", cmd.c_str());
7714         String outbuf, errbuf;
7715         if (!executeCommand(cmd.c_str(), "", outbuf, errbuf))
7716             {
7717             error("LINK problem: %s", errbuf.c_str());
7718             return false;
7719             }
7721         if (symFileName.size()>0)
7722             {
7723             String symFullName = parent.resolve(symFileName);
7724             cmd = objcopyCommand;
7725             cmd.append(" --only-keep-debug ");
7726             cmd.append(getNativePath(fullTarget));
7727             cmd.append(" ");
7728             cmd.append(getNativePath(symFullName));
7729             if (!executeCommand(cmd, "", outbuf, errbuf))
7730                 {
7731                 error("<strip> symbol file failed : %s", errbuf.c_str());
7732                 return false;
7733                 }
7734             }
7735             
7736         if (doStrip)
7737             {
7738             cmd = stripCommand;
7739             cmd.append(" ");
7740             cmd.append(getNativePath(fullTarget));
7741             if (!executeCommand(cmd, "", outbuf, errbuf))
7742                {
7743                error("<strip> failed : %s", errbuf.c_str());
7744                return false;
7745                }
7746             }
7748         return true;
7749         }
7751     virtual bool parse(Element *elem)
7752         {
7753         if (!parent.getAttribute(elem, "command", commandOpt))
7754             return false;
7755         if (!parent.getAttribute(elem, "objcopycommand", objcopyCommandOpt))
7756             return false;
7757         if (!parent.getAttribute(elem, "stripcommand", stripCommandOpt))
7758             return false;
7759         if (!parent.getAttribute(elem, "out", fileNameOpt))
7760             return false;
7761         if (!parent.getAttribute(elem, "strip", doStripOpt))
7762             return false;
7763         if (!parent.getAttribute(elem, "symfile", symFileNameOpt))
7764             return false;
7765             
7766         std::vector<Element *> children = elem->getChildren();
7767         for (unsigned int i=0 ; i<children.size() ; i++)
7768             {
7769             Element *child = children[i];
7770             String tagName = child->getName();
7771             if (tagName == "fileset")
7772                 {
7773                 if (!parseFileSet(child, parent, fileSet))
7774                     return false;
7775                 }
7776             else if (tagName == "flags")
7777                 {
7778                 if (!parent.getValue(child, flagsOpt))
7779                     return false;
7780                 flagsOpt = strip(flagsOpt);
7781                 }
7782             else if (tagName == "libs")
7783                 {
7784                 if (!parent.getValue(child, libsOpt))
7785                     return false;
7786                 libsOpt = strip(libsOpt);
7787                 }
7788             }
7789         return true;
7790         }
7792 private:
7794     FileSet fileSet;
7796     String  commandOpt;
7797     String  fileNameOpt;
7798     String  flagsOpt;
7799     String  libsOpt;
7800     String  doStripOpt;
7801     String  symFileNameOpt;
7802     String  stripCommandOpt;
7803     String  objcopyCommandOpt;
7805 };
7809 /**
7810  * Create a named file
7811  */
7812 class TaskMakeFile : public Task
7814 public:
7816     TaskMakeFile(MakeBase &par) : Task(par)
7817         { type = TASK_MAKEFILE; name = "makefile"; }
7819     virtual ~TaskMakeFile()
7820         {}
7822     virtual bool execute()
7823         {
7824         String fileName = parent.eval(fileNameOpt, "");
7825         String text     = parent.eval(textOpt, "");
7827         taskstatus("%s", fileName.c_str());
7828         String fullName = parent.resolve(fileName);
7829         if (!isNewerThan(parent.getURI().getPath(), fullName))
7830             {
7831             //trace("skipped <makefile>");
7832             return true;
7833             }
7834         String fullNative = getNativePath(fullName);
7835         //trace("fullName:%s", fullName.c_str());
7836         FILE *f = fopen(fullNative.c_str(), "w");
7837         if (!f)
7838             {
7839             error("<makefile> could not open %s for writing : %s",
7840                 fullName.c_str(), strerror(errno));
7841             return false;
7842             }
7843         for (unsigned int i=0 ; i<text.size() ; i++)
7844             fputc(text[i], f);
7845         fputc('\n', f);
7846         fclose(f);
7847         return true;
7848         }
7850     virtual bool parse(Element *elem)
7851         {
7852         if (!parent.getAttribute(elem, "file", fileNameOpt))
7853             return false;
7854         if (fileNameOpt.size() == 0)
7855             {
7856             error("<makefile> requires 'file=\"filename\"' attribute");
7857             return false;
7858             }
7859         if (!parent.getValue(elem, textOpt))
7860             return false;
7861         textOpt = leftJustify(textOpt);
7862         //trace("dirname:%s", dirName.c_str());
7863         return true;
7864         }
7866 private:
7868     String fileNameOpt;
7869     String textOpt;
7870 };
7874 /**
7875  * Create a named directory
7876  */
7877 class TaskMkDir : public Task
7879 public:
7881     TaskMkDir(MakeBase &par) : Task(par)
7882         { type = TASK_MKDIR; name = "mkdir"; }
7884     virtual ~TaskMkDir()
7885         {}
7887     virtual bool execute()
7888         {
7889         String dirName = parent.eval(dirNameOpt, ".");
7890         
7891         taskstatus("%s", dirName.c_str());
7892         String fullDir = parent.resolve(dirName);
7893         //trace("fullDir:%s", fullDir.c_str());
7894         if (!createDirectory(fullDir))
7895             return false;
7896         return true;
7897         }
7899     virtual bool parse(Element *elem)
7900         {
7901         if (!parent.getAttribute(elem, "dir", dirNameOpt))
7902             return false;
7903         if (dirNameOpt.size() == 0)
7904             {
7905             error("<mkdir> requires 'dir=\"dirname\"' attribute");
7906             return false;
7907             }
7908         return true;
7909         }
7911 private:
7913     String dirNameOpt;
7914 };
7918 /**
7919  * Create a named directory
7920  */
7921 class TaskMsgFmt: public Task
7923 public:
7925     TaskMsgFmt(MakeBase &par) : Task(par)
7926          { type = TASK_MSGFMT;  name = "msgfmt"; }
7928     virtual ~TaskMsgFmt()
7929         {}
7931     virtual bool execute()
7932         {
7933         String  command   = parent.eval(commandOpt, "msgfmt");
7934         String  toDirName = parent.eval(toDirNameOpt, ".");
7935         String  outName   = parent.eval(outNameOpt, "");
7936         bool    owndir    = parent.evalBool(owndirOpt, false);
7938         if (!listFiles(parent, fileSet))
7939             return false;
7940         String fileSetDir = fileSet.getDirectory();
7942         //trace("msgfmt: %d", fileSet.size());
7943         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7944             {
7945             String fileName = fileSet[i];
7946             if (getSuffix(fileName) != "po")
7947                 continue;
7948             String sourcePath;
7949             if (fileSetDir.size()>0)
7950                 {
7951                 sourcePath.append(fileSetDir);
7952                 sourcePath.append("/");
7953                 }
7954             sourcePath.append(fileName);
7955             String fullSource = parent.resolve(sourcePath);
7957             String destPath;
7958             if (toDirName.size()>0)
7959                 {
7960                 destPath.append(toDirName);
7961                 destPath.append("/");
7962                 }
7963             if (owndir)
7964                 {
7965                 String subdir = fileName;
7966                 unsigned int pos = subdir.find_last_of('.');
7967                 if (pos != subdir.npos)
7968                     subdir = subdir.substr(0, pos);
7969                 destPath.append(subdir);
7970                 destPath.append("/");
7971                 }
7972             //Pick the output file name
7973             if (outName.size() > 0)
7974                 {
7975                 destPath.append(outName);
7976                 }
7977             else
7978                 {
7979                 destPath.append(fileName);
7980                 destPath[destPath.size()-2] = 'm';
7981                 }
7983             String fullDest = parent.resolve(destPath);
7985             if (!isNewerThan(fullSource, fullDest))
7986                 {
7987                 //trace("skip %s", fullSource.c_str());
7988                 continue;
7989                 }
7990                 
7991             String cmd = command;
7992             cmd.append(" ");
7993             cmd.append(fullSource);
7994             cmd.append(" -o ");
7995             cmd.append(fullDest);
7996             
7997             int pos = fullDest.find_last_of('/');
7998             if (pos>0)
7999                 {
8000                 String fullDestPath = fullDest.substr(0, pos);
8001                 if (!createDirectory(fullDestPath))
8002                     return false;
8003                 }
8007             String outString, errString;
8008             if (!executeCommand(cmd.c_str(), "", outString, errString))
8009                 {
8010                 error("<msgfmt> problem: %s", errString.c_str());
8011                 return false;
8012                 }
8013             }
8015         return true;
8016         }
8018     virtual bool parse(Element *elem)
8019         {
8020         if (!parent.getAttribute(elem, "command", commandOpt))
8021             return false;
8022         if (!parent.getAttribute(elem, "todir", toDirNameOpt))
8023             return false;
8024         if (!parent.getAttribute(elem, "out", outNameOpt))
8025             return false;
8026         if (!parent.getAttribute(elem, "owndir", owndirOpt))
8027             return false;
8028             
8029         std::vector<Element *> children = elem->getChildren();
8030         for (unsigned int i=0 ; i<children.size() ; i++)
8031             {
8032             Element *child = children[i];
8033             String tagName = child->getName();
8034             if (tagName == "fileset")
8035                 {
8036                 if (!parseFileSet(child, parent, fileSet))
8037                     return false;
8038                 }
8039             }
8040         return true;
8041         }
8043 private:
8045     FileSet fileSet;
8047     String  commandOpt;
8048     String  toDirNameOpt;
8049     String  outNameOpt;
8050     String  owndirOpt;
8052 };
8056 /**
8057  *  Perform a Package-Config query similar to pkg-config
8058  */
8059 class TaskPkgConfig : public Task
8061 public:
8063     typedef enum
8064         {
8065         PKG_CONFIG_QUERY_CFLAGS,
8066         PKG_CONFIG_QUERY_LIBS,
8067         PKG_CONFIG_QUERY_ALL
8068         } QueryTypes;
8070     TaskPkgConfig(MakeBase &par) : Task(par)
8071         {
8072         type = TASK_PKG_CONFIG;
8073         name = "pkg-config";
8074         }
8076     virtual ~TaskPkgConfig()
8077         {}
8079     virtual bool execute()
8080         {
8081         String pkgName       = parent.eval(pkgNameOpt,      "");
8082         String prefix        = parent.eval(prefixOpt,       "");
8083         String propName      = parent.eval(propNameOpt,     "");
8084         String pkgConfigPath = parent.eval(pkgConfigPathOpt,"");
8085         String query         = parent.eval(queryOpt,        "all");
8087         String path = parent.resolve(pkgConfigPath);
8088         PkgConfig pkgconfig;
8089         pkgconfig.setPath(path);
8090         pkgconfig.setPrefix(prefix);
8091         if (!pkgconfig.query(pkgName))
8092             {
8093             error("<pkg-config> query failed for '%s", name.c_str());
8094             return false;
8095             }
8096             
8097         String val = "";
8098         if (query == "cflags")
8099             val = pkgconfig.getCflags();
8100         else if (query == "libs")
8101             val =pkgconfig.getLibs();
8102         else if (query == "all")
8103             val = pkgconfig.getAll();
8104         else
8105             {
8106             error("<pkg-config> unhandled query : %s", query.c_str());
8107             return false;
8108             }
8109         taskstatus("property %s = '%s'", propName.c_str(), val.c_str());
8110         parent.setProperty(propName, val);
8111         return true;
8112         }
8114     virtual bool parse(Element *elem)
8115         {
8116         //# NAME
8117         if (!parent.getAttribute(elem, "name", pkgNameOpt))
8118             return false;
8119         if (pkgNameOpt.size()==0)
8120             {
8121             error("<pkg-config> requires 'name=\"package\"' attribute");
8122             return false;
8123             }
8125         //# PROPERTY
8126         if (!parent.getAttribute(elem, "property", propNameOpt))
8127             return false;
8128         if (propNameOpt.size()==0)
8129             {
8130             error("<pkg-config> requires 'property=\"name\"' attribute");
8131             return false;
8132             }
8133         //# PATH
8134         if (!parent.getAttribute(elem, "path", pkgConfigPathOpt))
8135             return false;
8136         //# PREFIX
8137         if (!parent.getAttribute(elem, "prefix", prefixOpt))
8138             return false;
8139         //# QUERY
8140         if (!parent.getAttribute(elem, "query", queryOpt))
8141             return false;
8143         return true;
8144         }
8146 private:
8148     String queryOpt;
8149     String pkgNameOpt;
8150     String prefixOpt;
8151     String propNameOpt;
8152     String pkgConfigPathOpt;
8154 };
8161 /**
8162  *  Process an archive to allow random access
8163  */
8164 class TaskRanlib : public Task
8166 public:
8168     TaskRanlib(MakeBase &par) : Task(par)
8169         { type = TASK_RANLIB; name = "ranlib"; }
8171     virtual ~TaskRanlib()
8172         {}
8174     virtual bool execute()
8175         {
8176         String fileName = parent.eval(fileNameOpt, "");
8177         String command  = parent.eval(commandOpt, "ranlib");
8179         String fullName = parent.resolve(fileName);
8180         //trace("fullDir:%s", fullDir.c_str());
8181         String cmd = command;
8182         cmd.append(" ");
8183         cmd.append(fullName);
8184         String outbuf, errbuf;
8185         if (!executeCommand(cmd, "", outbuf, errbuf))
8186             return false;
8187         return true;
8188         }
8190     virtual bool parse(Element *elem)
8191         {
8192         if (!parent.getAttribute(elem, "command", commandOpt))
8193             return false;
8194         if (!parent.getAttribute(elem, "file", fileNameOpt))
8195             return false;
8196         if (fileNameOpt.size() == 0)
8197             {
8198             error("<ranlib> requires 'file=\"fileNname\"' attribute");
8199             return false;
8200             }
8201         return true;
8202         }
8204 private:
8206     String fileNameOpt;
8207     String commandOpt;
8208 };
8212 /**
8213  * Compile a resource file into a binary object
8214  */
8215 class TaskRC : public Task
8217 public:
8219     TaskRC(MakeBase &par) : Task(par)
8220         { type = TASK_RC; name = "rc"; }
8222     virtual ~TaskRC()
8223         {}
8225     virtual bool execute()
8226         {
8227         String command  = parent.eval(commandOpt,  "windres");
8228         String flags    = parent.eval(flagsOpt,    "");
8229         String fileName = parent.eval(fileNameOpt, "");
8230         String outName  = parent.eval(outNameOpt,  "");
8232         String fullFile = parent.resolve(fileName);
8233         String fullOut  = parent.resolve(outName);
8234         if (!isNewerThan(fullFile, fullOut))
8235             return true;
8236         String cmd = command;
8237         cmd.append(" -o ");
8238         cmd.append(fullOut);
8239         cmd.append(" ");
8240         cmd.append(flags);
8241         cmd.append(" ");
8242         cmd.append(fullFile);
8244         String outString, errString;
8245         if (!executeCommand(cmd.c_str(), "", outString, errString))
8246             {
8247             error("RC problem: %s", errString.c_str());
8248             return false;
8249             }
8250         return true;
8251         }
8253     virtual bool parse(Element *elem)
8254         {
8255         if (!parent.getAttribute(elem, "command", commandOpt))
8256             return false;
8257         if (!parent.getAttribute(elem, "file", fileNameOpt))
8258             return false;
8259         if (!parent.getAttribute(elem, "out", outNameOpt))
8260             return false;
8261         std::vector<Element *> children = elem->getChildren();
8262         for (unsigned int i=0 ; i<children.size() ; i++)
8263             {
8264             Element *child = children[i];
8265             String tagName = child->getName();
8266             if (tagName == "flags")
8267                 {
8268                 if (!parent.getValue(child, flagsOpt))
8269                     return false;
8270                 }
8271             }
8272         return true;
8273         }
8275 private:
8277     String commandOpt;
8278     String flagsOpt;
8279     String fileNameOpt;
8280     String outNameOpt;
8282 };
8286 /**
8287  *  Collect .o's into a .so or DLL
8288  */
8289 class TaskSharedLib : public Task
8291 public:
8293     TaskSharedLib(MakeBase &par) : Task(par)
8294         { type = TASK_SHAREDLIB; name = "dll"; }
8296     virtual ~TaskSharedLib()
8297         {}
8299     virtual bool execute()
8300         {
8301         String command     = parent.eval(commandOpt, "dllwrap");
8302         String fileName    = parent.eval(fileNameOpt, "");
8303         String defFileName = parent.eval(defFileNameOpt, "");
8304         String impFileName = parent.eval(impFileNameOpt, "");
8305         String libs        = parent.eval(libsOpt, "");
8307         //trace("###########HERE %d", fileSet.size());
8308         bool doit = false;
8309         
8310         String fullOut = parent.resolve(fileName);
8311         //trace("ar fullout: %s", fullOut.c_str());
8312         
8313         if (!listFiles(parent, fileSet))
8314             return false;
8315         String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
8317         for (unsigned int i=0 ; i<fileSet.size() ; i++)
8318             {
8319             String fname;
8320             if (fileSetDir.size()>0)
8321                 {
8322                 fname.append(fileSetDir);
8323                 fname.append("/");
8324                 }
8325             fname.append(fileSet[i]);
8326             String fullName = parent.resolve(fname);
8327             //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
8328             if (isNewerThan(fullName, fullOut))
8329                 doit = true;
8330             }
8331         //trace("Needs it:%d", doit);
8332         if (!doit)
8333             {
8334             return true;
8335             }
8337         String cmd = "dllwrap";
8338         cmd.append(" -o ");
8339         cmd.append(fullOut);
8340         if (defFileName.size()>0)
8341             {
8342             cmd.append(" --def ");
8343             cmd.append(defFileName);
8344             cmd.append(" ");
8345             }
8346         if (impFileName.size()>0)
8347             {
8348             cmd.append(" --implib ");
8349             cmd.append(impFileName);
8350             cmd.append(" ");
8351             }
8352         for (unsigned int i=0 ; i<fileSet.size() ; i++)
8353             {
8354             String fname;
8355             if (fileSetDir.size()>0)
8356                 {
8357                 fname.append(fileSetDir);
8358                 fname.append("/");
8359                 }
8360             fname.append(fileSet[i]);
8361             String fullName = parent.resolve(fname);
8363             cmd.append(" ");
8364             cmd.append(fullName);
8365             }
8366         cmd.append(" ");
8367         cmd.append(libs);
8369         String outString, errString;
8370         if (!executeCommand(cmd.c_str(), "", outString, errString))
8371             {
8372             error("<sharedlib> problem: %s", errString.c_str());
8373             return false;
8374             }
8376         return true;
8377         }
8379     virtual bool parse(Element *elem)
8380         {
8381         if (!parent.getAttribute(elem, "command", commandOpt))
8382             return false;
8383         if (!parent.getAttribute(elem, "file", fileNameOpt))
8384             return false;
8385         if (!parent.getAttribute(elem, "import", impFileNameOpt))
8386             return false;
8387         if (!parent.getAttribute(elem, "def", defFileNameOpt))
8388             return false;
8389             
8390         std::vector<Element *> children = elem->getChildren();
8391         for (unsigned int i=0 ; i<children.size() ; i++)
8392             {
8393             Element *child = children[i];
8394             String tagName = child->getName();
8395             if (tagName == "fileset")
8396                 {
8397                 if (!parseFileSet(child, parent, fileSet))
8398                     return false;
8399                 }
8400             else if (tagName == "libs")
8401                 {
8402                 if (!parent.getValue(child, libsOpt))
8403                     return false;
8404                 libsOpt = strip(libsOpt);
8405                 }
8406             }
8407         return true;
8408         }
8410 private:
8412     FileSet fileSet;
8414     String commandOpt;
8415     String fileNameOpt;
8416     String defFileNameOpt;
8417     String impFileNameOpt;
8418     String libsOpt;
8420 };
8424 /**
8425  * Run the "ar" command to archive .o's into a .a
8426  */
8427 class TaskStaticLib : public Task
8429 public:
8431     TaskStaticLib(MakeBase &par) : Task(par)
8432         { type = TASK_STATICLIB; name = "staticlib"; }
8434     virtual ~TaskStaticLib()
8435         {}
8437     virtual bool execute()
8438         {
8439         String command = parent.eval(commandOpt, "ar crv");
8440         String fileName = parent.eval(fileNameOpt, "");
8442         bool doit = false;
8443         
8444         String fullOut = parent.resolve(fileName);
8445         //trace("ar fullout: %s", fullOut.c_str());
8446         
8447         if (!listFiles(parent, fileSet))
8448             return false;
8449         String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
8450         //trace("###########HERE %s", fileSetDir.c_str());
8452         for (unsigned int i=0 ; i<fileSet.size() ; i++)
8453             {
8454             String fname;
8455             if (fileSetDir.size()>0)
8456                 {
8457                 fname.append(fileSetDir);
8458                 fname.append("/");
8459                 }
8460             fname.append(fileSet[i]);
8461             String fullName = parent.resolve(fname);
8462             //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
8463             if (isNewerThan(fullName, fullOut))
8464                 doit = true;
8465             }
8466         //trace("Needs it:%d", doit);
8467         if (!doit)
8468             {
8469             return true;
8470             }
8472         String cmd = command;
8473         cmd.append(" ");
8474         cmd.append(fullOut);
8475         for (unsigned int i=0 ; i<fileSet.size() ; i++)
8476             {
8477             String fname;
8478             if (fileSetDir.size()>0)
8479                 {
8480                 fname.append(fileSetDir);
8481                 fname.append("/");
8482                 }
8483             fname.append(fileSet[i]);
8484             String fullName = parent.resolve(fname);
8486             cmd.append(" ");
8487             cmd.append(fullName);
8488             }
8490         String outString, errString;
8491         if (!executeCommand(cmd.c_str(), "", outString, errString))
8492             {
8493             error("<staticlib> problem: %s", errString.c_str());
8494             return false;
8495             }
8497         return true;
8498         }
8501     virtual bool parse(Element *elem)
8502         {
8503         if (!parent.getAttribute(elem, "command", commandOpt))
8504             return false;
8505         if (!parent.getAttribute(elem, "file", fileNameOpt))
8506             return false;
8507             
8508         std::vector<Element *> children = elem->getChildren();
8509         for (unsigned int i=0 ; i<children.size() ; i++)
8510             {
8511             Element *child = children[i];
8512             String tagName = child->getName();
8513             if (tagName == "fileset")
8514                 {
8515                 if (!parseFileSet(child, parent, fileSet))
8516                     return false;
8517                 }
8518             }
8519         return true;
8520         }
8522 private:
8524     FileSet fileSet;
8526     String commandOpt;
8527     String fileNameOpt;
8529 };
8534 /**
8535  * Strip an executable
8536  */
8537 class TaskStrip : public Task
8539 public:
8541     TaskStrip(MakeBase &par) : Task(par)
8542         { type = TASK_STRIP; name = "strip"; }
8544     virtual ~TaskStrip()
8545         {}
8547     virtual bool execute()
8548         {
8549         String command     = parent.eval(commandOpt, "strip");
8550         String fileName    = parent.eval(fileNameOpt, "");
8551         String symFileName = parent.eval(symFileNameOpt, "");
8553         String fullName = parent.resolve(fileName);
8554         //trace("fullDir:%s", fullDir.c_str());
8555         String cmd;
8556         String outbuf, errbuf;
8558         if (symFileName.size()>0)
8559             {
8560             String symFullName = parent.resolve(symFileName);
8561             cmd = "objcopy --only-keep-debug ";
8562             cmd.append(getNativePath(fullName));
8563             cmd.append(" ");
8564             cmd.append(getNativePath(symFullName));
8565             if (!executeCommand(cmd, "", outbuf, errbuf))
8566                 {
8567                 error("<strip> symbol file failed : %s", errbuf.c_str());
8568                 return false;
8569                 }
8570             }
8571             
8572         cmd = command;
8573         cmd.append(getNativePath(fullName));
8574         if (!executeCommand(cmd, "", outbuf, errbuf))
8575             {
8576             error("<strip> failed : %s", errbuf.c_str());
8577             return false;
8578             }
8579         return true;
8580         }
8582     virtual bool parse(Element *elem)
8583         {
8584         if (!parent.getAttribute(elem, "command", commandOpt))
8585             return false;
8586         if (!parent.getAttribute(elem, "file", fileNameOpt))
8587             return false;
8588         if (!parent.getAttribute(elem, "symfile", symFileNameOpt))
8589             return false;
8590         if (fileNameOpt.size() == 0)
8591             {
8592             error("<strip> requires 'file=\"fileName\"' attribute");
8593             return false;
8594             }
8595         return true;
8596         }
8598 private:
8600     String commandOpt;
8601     String fileNameOpt;
8602     String symFileNameOpt;
8603 };
8606 /**
8607  *
8608  */
8609 class TaskTouch : public Task
8611 public:
8613     TaskTouch(MakeBase &par) : Task(par)
8614         { type = TASK_TOUCH; name = "touch"; }
8616     virtual ~TaskTouch()
8617         {}
8619     virtual bool execute()
8620         {
8621         String fileName = parent.eval(fileNameOpt, "");
8623         String fullName = parent.resolve(fileName);
8624         String nativeFile = getNativePath(fullName);
8625         if (!isRegularFile(fullName) && !isDirectory(fullName))
8626             {            
8627             // S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH
8628             int ret = creat(nativeFile.c_str(), 0666);
8629             if (ret != 0) 
8630                 {
8631                 error("<touch> could not create '%s' : %s",
8632                     nativeFile.c_str(), strerror(ret));
8633                 return false;
8634                 }
8635             return true;
8636             }
8637         int ret = utime(nativeFile.c_str(), (struct utimbuf *)0);
8638         if (ret != 0)
8639             {
8640             error("<touch> could not update the modification time for '%s' : %s",
8641                 nativeFile.c_str(), strerror(ret));
8642             return false;
8643             }
8644         return true;
8645         }
8647     virtual bool parse(Element *elem)
8648         {
8649         //trace("touch parse");
8650         if (!parent.getAttribute(elem, "file", fileNameOpt))
8651             return false;
8652         if (fileNameOpt.size() == 0)
8653             {
8654             error("<touch> requires 'file=\"fileName\"' attribute");
8655             return false;
8656             }
8657         return true;
8658         }
8660     String fileNameOpt;
8661 };
8664 /**
8665  *
8666  */
8667 class TaskTstamp : public Task
8669 public:
8671     TaskTstamp(MakeBase &par) : Task(par)
8672         { type = TASK_TSTAMP; name = "tstamp"; }
8674     virtual ~TaskTstamp()
8675         {}
8677     virtual bool execute()
8678         {
8679         return true;
8680         }
8682     virtual bool parse(Element *elem)
8683         {
8684         //trace("tstamp parse");
8685         return true;
8686         }
8687 };
8691 /**
8692  *
8693  */
8694 Task *Task::createTask(Element *elem, int lineNr)
8696     String tagName = elem->getName();
8697     //trace("task:%s", tagName.c_str());
8698     Task *task = NULL;
8699     if (tagName == "cc")
8700         task = new TaskCC(parent);
8701     else if (tagName == "copy")
8702         task = new TaskCopy(parent);
8703     else if (tagName == "cxxtestpart")
8704         task = new TaskCxxTestPart(parent);
8705     else if (tagName == "cxxtestroot")
8706         task = new TaskCxxTestRoot(parent);
8707     else if (tagName == "delete")
8708         task = new TaskDelete(parent);
8709     else if (tagName == "echo")
8710         task = new TaskEcho(parent);
8711     else if (tagName == "jar")
8712         task = new TaskJar(parent);
8713     else if (tagName == "javac")
8714         task = new TaskJavac(parent);
8715     else if (tagName == "link")
8716         task = new TaskLink(parent);
8717     else if (tagName == "makefile")
8718         task = new TaskMakeFile(parent);
8719     else if (tagName == "mkdir")
8720         task = new TaskMkDir(parent);
8721     else if (tagName == "msgfmt")
8722         task = new TaskMsgFmt(parent);
8723     else if (tagName == "pkg-config")
8724         task = new TaskPkgConfig(parent);
8725     else if (tagName == "ranlib")
8726         task = new TaskRanlib(parent);
8727     else if (tagName == "rc")
8728         task = new TaskRC(parent);
8729     else if (tagName == "sharedlib")
8730         task = new TaskSharedLib(parent);
8731     else if (tagName == "staticlib")
8732         task = new TaskStaticLib(parent);
8733     else if (tagName == "strip")
8734         task = new TaskStrip(parent);
8735     else if (tagName == "touch")
8736         task = new TaskTouch(parent);
8737     else if (tagName == "tstamp")
8738         task = new TaskTstamp(parent);
8739     else
8740         {
8741         error("Unknown task '%s'", tagName.c_str());
8742         return NULL;
8743         }
8745     task->setLine(lineNr);
8747     if (!task->parse(elem))
8748         {
8749         delete task;
8750         return NULL;
8751         }
8752     return task;
8757 //########################################################################
8758 //# T A R G E T
8759 //########################################################################
8761 /**
8762  *
8763  */
8764 class Target : public MakeBase
8767 public:
8769     /**
8770      *
8771      */
8772     Target(Make &par) : parent(par)
8773         { init(); }
8775     /**
8776      *
8777      */
8778     Target(const Target &other) : parent(other.parent)
8779         { init(); assign(other); }
8781     /**
8782      *
8783      */
8784     Target &operator=(const Target &other)
8785         { init(); assign(other); return *this; }
8787     /**
8788      *
8789      */
8790     virtual ~Target()
8791         { cleanup() ; }
8794     /**
8795      *
8796      */
8797     virtual Make &getParent()
8798         { return parent; }
8800     /**
8801      *
8802      */
8803     virtual String getName()
8804         { return name; }
8806     /**
8807      *
8808      */
8809     virtual void setName(const String &val)
8810         { name = val; }
8812     /**
8813      *
8814      */
8815     virtual String getDescription()
8816         { return description; }
8818     /**
8819      *
8820      */
8821     virtual void setDescription(const String &val)
8822         { description = val; }
8824     /**
8825      *
8826      */
8827     virtual void addDependency(const String &val)
8828         { deps.push_back(val); }
8830     /**
8831      *
8832      */
8833     virtual void parseDependencies(const String &val)
8834         { deps = tokenize(val, ", "); }
8836     /**
8837      *
8838      */
8839     virtual std::vector<String> &getDependencies()
8840         { return deps; }
8842     /**
8843      *
8844      */
8845     virtual String getIf()
8846         { return ifVar; }
8848     /**
8849      *
8850      */
8851     virtual void setIf(const String &val)
8852         { ifVar = val; }
8854     /**
8855      *
8856      */
8857     virtual String getUnless()
8858         { return unlessVar; }
8860     /**
8861      *
8862      */
8863     virtual void setUnless(const String &val)
8864         { unlessVar = val; }
8866     /**
8867      *
8868      */
8869     virtual void addTask(Task *val)
8870         { tasks.push_back(val); }
8872     /**
8873      *
8874      */
8875     virtual std::vector<Task *> &getTasks()
8876         { return tasks; }
8878 private:
8880     void init()
8881         {
8882         }
8884     void cleanup()
8885         {
8886         tasks.clear();
8887         }
8889     void assign(const Target &other)
8890         {
8891         //parent      = other.parent;
8892         name        = other.name;
8893         description = other.description;
8894         ifVar       = other.ifVar;
8895         unlessVar   = other.unlessVar;
8896         deps        = other.deps;
8897         tasks       = other.tasks;
8898         }
8900     Make &parent;
8902     String name;
8904     String description;
8906     String ifVar;
8908     String unlessVar;
8910     std::vector<String> deps;
8912     std::vector<Task *> tasks;
8914 };
8923 //########################################################################
8924 //# M A K E
8925 //########################################################################
8928 /**
8929  *
8930  */
8931 class Make : public MakeBase
8934 public:
8936     /**
8937      *
8938      */
8939     Make()
8940         { init(); }
8942     /**
8943      *
8944      */
8945     Make(const Make &other)
8946         { assign(other); }
8948     /**
8949      *
8950      */
8951     Make &operator=(const Make &other)
8952         { assign(other); return *this; }
8954     /**
8955      *
8956      */
8957     virtual ~Make()
8958         { cleanup(); }
8960     /**
8961      *
8962      */
8963     virtual std::map<String, Target> &getTargets()
8964         { return targets; }
8967     /**
8968      *
8969      */
8970     virtual String version()
8971         { return BUILDTOOL_VERSION; }
8973     /**
8974      * Overload a <property>
8975      */
8976     virtual bool specifyProperty(const String &name,
8977                                  const String &value);
8979     /**
8980      *
8981      */
8982     virtual bool run();
8984     /**
8985      *
8986      */
8987     virtual bool run(const String &target);
8991 private:
8993     /**
8994      *
8995      */
8996     void init();
8998     /**
8999      *
9000      */
9001     void cleanup();
9003     /**
9004      *
9005      */
9006     void assign(const Make &other);
9008     /**
9009      *
9010      */
9011     bool executeTask(Task &task);
9014     /**
9015      *
9016      */
9017     bool executeTarget(Target &target,
9018              std::set<String> &targetsCompleted);
9021     /**
9022      *
9023      */
9024     bool execute();
9026     /**
9027      *
9028      */
9029     bool checkTargetDependencies(Target &prop,
9030                     std::vector<String> &depList);
9032     /**
9033      *
9034      */
9035     bool parsePropertyFile(const String &fileName,
9036                            const String &prefix);
9038     /**
9039      *
9040      */
9041     bool parseProperty(Element *elem);
9043     /**
9044      *
9045      */
9046     bool parseFile();
9048     /**
9049      *
9050      */
9051     std::vector<String> glob(const String &pattern);
9054     //###############
9055     //# Fields
9056     //###############
9058     String projectName;
9060     String currentTarget;
9062     String defaultTarget;
9064     String specifiedTarget;
9066     String baseDir;
9068     String description;
9069     
9070     //std::vector<Property> properties;
9071     
9072     std::map<String, Target> targets;
9074     std::vector<Task *> allTasks;
9075     
9076     std::map<String, String> specifiedProperties;
9078 };
9081 //########################################################################
9082 //# C L A S S  M A I N T E N A N C E
9083 //########################################################################
9085 /**
9086  *
9087  */
9088 void Make::init()
9090     uri             = "build.xml";
9091     projectName     = "";
9092     currentTarget   = "";
9093     defaultTarget   = "";
9094     specifiedTarget = "";
9095     baseDir         = "";
9096     description     = "";
9097     envPrefix       = "env.";
9098     pcPrefix        = "pc.";
9099     pccPrefix       = "pcc.";
9100     pclPrefix       = "pcl.";
9101     properties.clear();
9102     for (unsigned int i = 0 ; i < allTasks.size() ; i++)
9103         delete allTasks[i];
9104     allTasks.clear();
9109 /**
9110  *
9111  */
9112 void Make::cleanup()
9114     for (unsigned int i = 0 ; i < allTasks.size() ; i++)
9115         delete allTasks[i];
9116     allTasks.clear();
9121 /**
9122  *
9123  */
9124 void Make::assign(const Make &other)
9126     uri              = other.uri;
9127     projectName      = other.projectName;
9128     currentTarget    = other.currentTarget;
9129     defaultTarget    = other.defaultTarget;
9130     specifiedTarget  = other.specifiedTarget;
9131     baseDir          = other.baseDir;
9132     description      = other.description;
9133     properties       = other.properties;
9138 //########################################################################
9139 //# U T I L I T Y    T A S K S
9140 //########################################################################
9142 /**
9143  *  Perform a file globbing
9144  */
9145 std::vector<String> Make::glob(const String &pattern)
9147     std::vector<String> res;
9148     return res;
9152 //########################################################################
9153 //# P U B L I C    A P I
9154 //########################################################################
9158 /**
9159  *
9160  */
9161 bool Make::executeTarget(Target &target,
9162              std::set<String> &targetsCompleted)
9165     String name = target.getName();
9167     //First get any dependencies for this target
9168     std::vector<String> deps = target.getDependencies();
9169     for (unsigned int i=0 ; i<deps.size() ; i++)
9170         {
9171         String dep = deps[i];
9172         //Did we do it already?  Skip
9173         if (targetsCompleted.find(dep)!=targetsCompleted.end())
9174             continue;
9175             
9176         std::map<String, Target> &tgts =
9177                target.getParent().getTargets();
9178         std::map<String, Target>::iterator iter =
9179                tgts.find(dep);
9180         if (iter == tgts.end())
9181             {
9182             error("Target '%s' dependency '%s' not found",
9183                       name.c_str(),  dep.c_str());
9184             return false;
9185             }
9186         Target depTarget = iter->second;
9187         if (!executeTarget(depTarget, targetsCompleted))
9188             {
9189             return false;
9190             }
9191         }
9193     status("##### Target : %s\n##### %s", name.c_str(),
9194             target.getDescription().c_str());
9196     //Now let's do the tasks
9197     std::vector<Task *> &tasks = target.getTasks();
9198     for (unsigned int i=0 ; i<tasks.size() ; i++)
9199         {
9200         Task *task = tasks[i];
9201         status("--- %s / %s", name.c_str(), task->getName().c_str());
9202         if (!task->execute())
9203             {
9204             return false;
9205             }
9206         }
9207         
9208     targetsCompleted.insert(name);
9209     
9210     return true;
9215 /**
9216  *  Main execute() method.  Start here and work
9217  *  up the dependency tree 
9218  */
9219 bool Make::execute()
9221     status("######## EXECUTE");
9223     //Determine initial target
9224     if (specifiedTarget.size()>0)
9225         {
9226         currentTarget = specifiedTarget;
9227         }
9228     else if (defaultTarget.size()>0)
9229         {
9230         currentTarget = defaultTarget;
9231         }
9232     else
9233         {
9234         error("execute: no specified or default target requested");
9235         return false;
9236         }
9238     std::map<String, Target>::iterator iter =
9239                targets.find(currentTarget);
9240     if (iter == targets.end())
9241         {
9242         error("Initial target '%s' not found",
9243                  currentTarget.c_str());
9244         return false;
9245         }
9246         
9247     //Now run
9248     Target target = iter->second;
9249     std::set<String> targetsCompleted;
9250     if (!executeTarget(target, targetsCompleted))
9251         {
9252         return false;
9253         }
9255     status("######## EXECUTE COMPLETE");
9256     return true;
9262 /**
9263  *
9264  */
9265 bool Make::checkTargetDependencies(Target &target, 
9266                             std::vector<String> &depList)
9268     String tgtName = target.getName().c_str();
9269     depList.push_back(tgtName);
9271     std::vector<String> deps = target.getDependencies();
9272     for (unsigned int i=0 ; i<deps.size() ; i++)
9273         {
9274         String dep = deps[i];
9275         //First thing entered was the starting Target
9276         if (dep == depList[0])
9277             {
9278             error("Circular dependency '%s' found at '%s'",
9279                       dep.c_str(), tgtName.c_str());
9280             std::vector<String>::iterator diter;
9281             for (diter=depList.begin() ; diter!=depList.end() ; diter++)
9282                 {
9283                 error("  %s", diter->c_str());
9284                 }
9285             return false;
9286             }
9288         std::map<String, Target> &tgts =
9289                   target.getParent().getTargets();
9290         std::map<String, Target>::iterator titer = tgts.find(dep);
9291         if (titer == tgts.end())
9292             {
9293             error("Target '%s' dependency '%s' not found",
9294                       tgtName.c_str(), dep.c_str());
9295             return false;
9296             }
9297         if (!checkTargetDependencies(titer->second, depList))
9298             {
9299             return false;
9300             }
9301         }
9302     return true;
9309 static int getword(int pos, const String &inbuf, String &result)
9311     int p = pos;
9312     int len = (int)inbuf.size();
9313     String val;
9314     while (p < len)
9315         {
9316         char ch = inbuf[p];
9317         if (!isalnum(ch) && ch!='.' && ch!='_')
9318             break;
9319         val.push_back(ch);
9320         p++;
9321         }
9322     result = val;
9323     return p;
9329 /**
9330  *
9331  */
9332 bool Make::parsePropertyFile(const String &fileName,
9333                              const String &prefix)
9335     FILE *f = fopen(fileName.c_str(), "r");
9336     if (!f)
9337         {
9338         error("could not open property file %s", fileName.c_str());
9339         return false;
9340         }
9341     int linenr = 0;
9342     while (!feof(f))
9343         {
9344         char buf[256];
9345         if (!fgets(buf, 255, f))
9346             break;
9347         linenr++;
9348         String s = buf;
9349         s = trim(s);
9350         int len = s.size();
9351         if (len == 0)
9352             continue;
9353         if (s[0] == '#')
9354             continue;
9355         String key;
9356         String val;
9357         int p = 0;
9358         int p2 = getword(p, s, key);
9359         if (p2 <= p)
9360             {
9361             error("property file %s, line %d: expected keyword",
9362                     fileName.c_str(), linenr);
9363             return false;
9364             }
9365         if (prefix.size() > 0)
9366             {
9367             key.insert(0, prefix);
9368             }
9370         //skip whitespace
9371         for (p=p2 ; p<len ; p++)
9372             if (!isspace(s[p]))
9373                 break;
9375         if (p>=len || s[p]!='=')
9376             {
9377             error("property file %s, line %d: expected '='",
9378                     fileName.c_str(), linenr);
9379             return false;
9380             }
9381         p++;
9383         //skip whitespace
9384         for ( ; p<len ; p++)
9385             if (!isspace(s[p]))
9386                 break;
9388         /* This way expects a word after the =
9389         p2 = getword(p, s, val);
9390         if (p2 <= p)
9391             {
9392             error("property file %s, line %d: expected value",
9393                     fileName.c_str(), linenr);
9394             return false;
9395             }
9396         */
9397         // This way gets the rest of the line after the =
9398         if (p>=len)
9399             {
9400             error("property file %s, line %d: expected value",
9401                     fileName.c_str(), linenr);
9402             return false;
9403             }
9404         val = s.substr(p);
9405         if (key.size()==0)
9406             continue;
9407         //allow property to be set, even if val=""
9409         //trace("key:'%s' val:'%s'", key.c_str(), val.c_str());
9410         //See if we wanted to overload this property
9411         std::map<String, String>::iterator iter =
9412             specifiedProperties.find(key);
9413         if (iter!=specifiedProperties.end())
9414             {
9415             val = iter->second;
9416             status("overloading property '%s' = '%s'",
9417                    key.c_str(), val.c_str());
9418             }
9419         properties[key] = val;
9420         }
9421     fclose(f);
9422     return true;
9428 /**
9429  *
9430  */
9431 bool Make::parseProperty(Element *elem)
9433     std::vector<Attribute> &attrs = elem->getAttributes();
9434     for (unsigned int i=0 ; i<attrs.size() ; i++)
9435         {
9436         String attrName = attrs[i].getName();
9437         String attrVal  = attrs[i].getValue();
9439         if (attrName == "name")
9440             {
9441             String val;
9442             if (!getAttribute(elem, "value", val))
9443                 return false;
9444             if (val.size() > 0)
9445                 {
9446                 properties[attrVal] = val;
9447                 }
9448             else
9449                 {
9450                 if (!getAttribute(elem, "location", val))
9451                     return false;
9452                 //let the property exist, even if not defined
9453                 properties[attrVal] = val;
9454                 }
9455             //See if we wanted to overload this property
9456             std::map<String, String>::iterator iter =
9457                 specifiedProperties.find(attrVal);
9458             if (iter != specifiedProperties.end())
9459                 {
9460                 val = iter->second;
9461                 status("overloading property '%s' = '%s'",
9462                     attrVal.c_str(), val.c_str());
9463                 properties[attrVal] = val;
9464                 }
9465             }
9466         else if (attrName == "file")
9467             {
9468             String prefix;
9469             if (!getAttribute(elem, "prefix", prefix))
9470                 return false;
9471             if (prefix.size() > 0)
9472                 {
9473                 if (prefix[prefix.size()-1] != '.')
9474                     prefix.push_back('.');
9475                 }
9476             if (!parsePropertyFile(attrName, prefix))
9477                 return false;
9478             }
9479         else if (attrName == "environment")
9480             {
9481             if (attrVal.find('.') != attrVal.npos)
9482                 {
9483                 error("environment prefix cannot have a '.' in it");
9484                 return false;
9485                 }
9486             envPrefix = attrVal;
9487             envPrefix.push_back('.');
9488             }
9489         else if (attrName == "pkg-config")
9490             {
9491             if (attrVal.find('.') != attrVal.npos)
9492                 {
9493                 error("pkg-config prefix cannot have a '.' in it");
9494                 return false;
9495                 }
9496             pcPrefix = attrVal;
9497             pcPrefix.push_back('.');
9498             }
9499         else if (attrName == "pkg-config-cflags")
9500             {
9501             if (attrVal.find('.') != attrVal.npos)
9502                 {
9503                 error("pkg-config-cflags prefix cannot have a '.' in it");
9504                 return false;
9505                 }
9506             pccPrefix = attrVal;
9507             pccPrefix.push_back('.');
9508             }
9509         else if (attrName == "pkg-config-libs")
9510             {
9511             if (attrVal.find('.') != attrVal.npos)
9512                 {
9513                 error("pkg-config-libs prefix cannot have a '.' in it");
9514                 return false;
9515                 }
9516             pclPrefix = attrVal;
9517             pclPrefix.push_back('.');
9518             }
9519         }
9521     return true;
9527 /**
9528  *
9529  */
9530 bool Make::parseFile()
9532     status("######## PARSE : %s", uri.getPath().c_str());
9534     setLine(0);
9536     Parser parser;
9537     Element *root = parser.parseFile(uri.getNativePath());
9538     if (!root)
9539         {
9540         error("Could not open %s for reading",
9541               uri.getNativePath().c_str());
9542         return false;
9543         }
9544     
9545     setLine(root->getLine());
9547     if (root->getChildren().size()==0 ||
9548         root->getChildren()[0]->getName()!="project")
9549         {
9550         error("Main xml element should be <project>");
9551         delete root;
9552         return false;
9553         }
9555     //########## Project attributes
9556     Element *project = root->getChildren()[0];
9557     String s = project->getAttribute("name");
9558     if (s.size() > 0)
9559         projectName = s;
9560     s = project->getAttribute("default");
9561     if (s.size() > 0)
9562         defaultTarget = s;
9563     s = project->getAttribute("basedir");
9564     if (s.size() > 0)
9565         baseDir = s;
9567     //######### PARSE MEMBERS
9568     std::vector<Element *> children = project->getChildren();
9569     for (unsigned int i=0 ; i<children.size() ; i++)
9570         {
9571         Element *elem = children[i];
9572         setLine(elem->getLine());
9573         String tagName = elem->getName();
9575         //########## DESCRIPTION
9576         if (tagName == "description")
9577             {
9578             description = parser.trim(elem->getValue());
9579             }
9581         //######### PROPERTY
9582         else if (tagName == "property")
9583             {
9584             if (!parseProperty(elem))
9585                 return false;
9586             }
9588         //######### TARGET
9589         else if (tagName == "target")
9590             {
9591             String tname   = elem->getAttribute("name");
9592             String tdesc   = elem->getAttribute("description");
9593             String tdeps   = elem->getAttribute("depends");
9594             String tif     = elem->getAttribute("if");
9595             String tunless = elem->getAttribute("unless");
9596             Target target(*this);
9597             target.setName(tname);
9598             target.setDescription(tdesc);
9599             target.parseDependencies(tdeps);
9600             target.setIf(tif);
9601             target.setUnless(tunless);
9602             std::vector<Element *> telems = elem->getChildren();
9603             for (unsigned int i=0 ; i<telems.size() ; i++)
9604                 {
9605                 Element *telem = telems[i];
9606                 Task breeder(*this);
9607                 Task *task = breeder.createTask(telem, telem->getLine());
9608                 if (!task)
9609                     return false;
9610                 allTasks.push_back(task);
9611                 target.addTask(task);
9612                 }
9614             //Check name
9615             if (tname.size() == 0)
9616                 {
9617                 error("no name for target");
9618                 return false;
9619                 }
9620             //Check for duplicate name
9621             if (targets.find(tname) != targets.end())
9622                 {
9623                 error("target '%s' already defined", tname.c_str());
9624                 return false;
9625                 }
9626             //more work than targets[tname]=target, but avoids default allocator
9627             targets.insert(std::make_pair<String, Target>(tname, target));
9628             }
9629         //######### none of the above
9630         else
9631             {
9632             error("unknown toplevel tag: <%s>", tagName.c_str());
9633             return false;
9634             }
9636         }
9638     std::map<String, Target>::iterator iter;
9639     for (iter = targets.begin() ; iter!= targets.end() ; iter++)
9640         {
9641         Target tgt = iter->second;
9642         std::vector<String> depList;
9643         if (!checkTargetDependencies(tgt, depList))
9644             {
9645             return false;
9646             }
9647         }
9650     delete root;
9651     status("######## PARSE COMPLETE");
9652     return true;
9656 /**
9657  * Overload a <property>
9658  */
9659 bool Make::specifyProperty(const String &name, const String &value)
9661     if (specifiedProperties.find(name) != specifiedProperties.end())
9662         {
9663         error("Property %s already specified", name.c_str());
9664         return false;
9665         }
9666     specifiedProperties[name] = value;
9667     return true;
9672 /**
9673  *
9674  */
9675 bool Make::run()
9677     if (!parseFile())
9678         return false;
9679         
9680     if (!execute())
9681         return false;
9683     return true;
9689 /**
9690  * Get a formatted MM:SS.sss time elapsed string
9691  */ 
9692 static String
9693 timeDiffString(struct timeval &x, struct timeval &y)
9695     long microsX  = x.tv_usec;
9696     long secondsX = x.tv_sec;
9697     long microsY  = y.tv_usec;
9698     long secondsY = y.tv_sec;
9699     if (microsX < microsY)
9700         {
9701         microsX += 1000000;
9702         secondsX -= 1;
9703         }
9705     int seconds = (int)(secondsX - secondsY);
9706     int millis  = (int)((microsX - microsY)/1000);
9708     int minutes = seconds/60;
9709     seconds -= minutes*60;
9710     char buf[80];
9711     snprintf(buf, 79, "%dm %d.%03ds", minutes, seconds, millis);
9712     String ret = buf;
9713     return ret;
9714     
9717 /**
9718  *
9719  */
9720 bool Make::run(const String &target)
9722     status("####################################################");
9723     status("#   %s", version().c_str());
9724     status("####################################################");
9725     struct timeval timeStart, timeEnd;
9726     ::gettimeofday(&timeStart, NULL);
9727     specifiedTarget = target;
9728     if (!run())
9729         return false;
9730     ::gettimeofday(&timeEnd, NULL);
9731     String timeStr = timeDiffString(timeEnd, timeStart);
9732     status("####################################################");
9733     status("#   BuildTool Completed : %s", timeStr.c_str());
9734     status("####################################################");
9735     return true;
9744 }// namespace buildtool
9745 //########################################################################
9746 //# M A I N
9747 //########################################################################
9749 typedef buildtool::String String;
9751 /**
9752  *  Format an error message in printf() style
9753  */
9754 static void error(const char *fmt, ...)
9756     va_list ap;
9757     va_start(ap, fmt);
9758     fprintf(stderr, "BuildTool error: ");
9759     vfprintf(stderr, fmt, ap);
9760     fprintf(stderr, "\n");
9761     va_end(ap);
9765 static bool parseProperty(const String &s, String &name, String &val)
9767     int len = s.size();
9768     int i;
9769     for (i=0 ; i<len ; i++)
9770         {
9771         char ch = s[i];
9772         if (ch == '=')
9773             break;
9774         name.push_back(ch);
9775         }
9776     if (i>=len || s[i]!='=')
9777         {
9778         error("property requires -Dname=value");
9779         return false;
9780         }
9781     i++;
9782     for ( ; i<len ; i++)
9783         {
9784         char ch = s[i];
9785         val.push_back(ch);
9786         }
9787     return true;
9791 /**
9792  * Compare a buffer with a key, for the length of the key
9793  */
9794 static bool sequ(const String &buf, const char *key)
9796     int len = buf.size();
9797     for (int i=0 ; key[i] && i<len ; i++)
9798         {
9799         if (key[i] != buf[i])
9800             return false;
9801         }        
9802     return true;
9805 static void usage(int argc, char **argv)
9807     printf("usage:\n");
9808     printf("   %s [options] [target]\n", argv[0]);
9809     printf("Options:\n");
9810     printf("  -help, -h              print this message\n");
9811     printf("  -version               print the version information and exit\n");
9812     printf("  -file <file>           use given buildfile\n");
9813     printf("  -f <file>                 ''\n");
9814     printf("  -D<property>=<value>   use value for given property\n");
9820 /**
9821  * Parse the command-line args, get our options,
9822  * and run this thing
9823  */   
9824 static bool parseOptions(int argc, char **argv)
9826     if (argc < 1)
9827         {
9828         error("Cannot parse arguments");
9829         return false;
9830         }
9832     buildtool::Make make;
9834     String target;
9836     //char *progName = argv[0];
9837     for (int i=1 ; i<argc ; i++)
9838         {
9839         String arg = argv[i];
9840         if (arg.size()>1 && arg[0]=='-')
9841             {
9842             if (arg == "-h" || arg == "-help")
9843                 {
9844                 usage(argc,argv);
9845                 return true;
9846                 }
9847             else if (arg == "-version")
9848                 {
9849                 printf("%s", make.version().c_str());
9850                 return true;
9851                 }
9852             else if (arg == "-f" || arg == "-file")
9853                 {
9854                 if (i>=argc)
9855                    {
9856                    usage(argc, argv);
9857                    return false;
9858                    }
9859                 i++; //eat option
9860                 make.setURI(argv[i]);
9861                 }
9862             else if (arg.size()>2 && sequ(arg, "-D"))
9863                 {
9864                 String s = arg.substr(2, arg.size());
9865                 String name, value;
9866                 if (!parseProperty(s, name, value))
9867                    {
9868                    usage(argc, argv);
9869                    return false;
9870                    }
9871                 if (!make.specifyProperty(name, value))
9872                     return false;
9873                 }
9874             else
9875                 {
9876                 error("Unknown option:%s", arg.c_str());
9877                 return false;
9878                 }
9879             }
9880         else
9881             {
9882             if (target.size()>0)
9883                 {
9884                 error("only one initial target");
9885                 usage(argc, argv);
9886                 return false;
9887                 }
9888             target = arg;
9889             }
9890         }
9892     //We have the options.  Now execute them
9893     if (!make.run(target))
9894         return false;
9896     return true;
9902 /*
9903 static bool runMake()
9905     buildtool::Make make;
9906     if (!make.run())
9907         return false;
9908     return true;
9912 static bool pkgConfigTest()
9914     buildtool::PkgConfig pkgConfig;
9915     if (!pkgConfig.readFile("gtk+-2.0.pc"))
9916         return false;
9917     return true;
9922 static bool depTest()
9924     buildtool::DepTool deptool;
9925     deptool.setSourceDirectory("/dev/ink/inkscape/src");
9926     if (!deptool.generateDependencies("build.dep"))
9927         return false;
9928     std::vector<buildtool::FileRec> res =
9929            deptool.loadDepFile("build.dep");
9930     if (res.size() == 0)
9931         return false;
9932     return true;
9935 static bool popenTest()
9937     buildtool::Make make;
9938     buildtool::String out, err;
9939     bool ret = make.executeCommand("gcc xx.cpp", "", out, err);
9940     printf("Popen test:%d '%s' '%s'\n", ret, out.c_str(), err.c_str());
9941     return true;
9945 static bool propFileTest()
9947     buildtool::Make make;
9948     make.parsePropertyFile("test.prop", "test.");
9949     return true;
9951 */
9953 int main(int argc, char **argv)
9956     if (!parseOptions(argc, argv))
9957         return 1;
9958     /*
9959     if (!popenTest())
9960         return 1;
9962     if (!depTest())
9963         return 1;
9964     if (!propFileTest())
9965         return 1;
9966     if (runMake())
9967         return 1;
9968     */
9969     return 0;
9973 //########################################################################
9974 //# E N D 
9975 //########################################################################