Code

Fixed version of stat cache for buildtool.cpp (now invalidates cache entries for...
[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 struct StatResult {
2749     int result;
2750     struct stat statInfo;
2751 };
2752 typedef std::map<String, StatResult> statCacheType;
2753 static statCacheType statCache;
2754 static int cachedStat(const String &f, struct stat *s) {
2755     //printf("Stat path: %s\n", f.c_str());
2756     std::pair<statCacheType::iterator, bool> result = statCache.insert(statCacheType::value_type(f, StatResult()));
2757     if (result.second) {
2758         result.first->second.result = stat(f.c_str(), &(result.first->second.statInfo));
2759     }
2760     *s = result.first->second.statInfo;
2761     return result.first->second.result;
2763 static void removeFromStatCache(const String f) {
2764     //printf("Removing from cache: %s\n", f.c_str());
2765     statCache.erase(f);
2768 //########################################################################
2769 //# F I L E S E T
2770 //########################################################################
2771 /**
2772  * This is the descriptor for a <fileset> item
2773  */
2774 class FileSet
2776 public:
2778     /**
2779      *
2780      */
2781     FileSet()
2782         {}
2784     /**
2785      *
2786      */
2787     FileSet(const FileSet &other)
2788         { assign(other); }
2790     /**
2791      *
2792      */
2793     FileSet &operator=(const FileSet &other)
2794         { assign(other); return *this; }
2796     /**
2797      *
2798      */
2799     virtual ~FileSet()
2800         {}
2802     /**
2803      *
2804      */
2805     String getDirectory() const
2806         { return directory; }
2807         
2808     /**
2809      *
2810      */
2811     void setDirectory(const String &val)
2812         { directory = val; }
2814     /**
2815      *
2816      */
2817     void setFiles(const std::vector<String> &val)
2818         { files = val; }
2820     /**
2821      *
2822      */
2823     std::vector<String> getFiles() const
2824         { return files; }
2825         
2826     /**
2827      *
2828      */
2829     void setIncludes(const std::vector<String> &val)
2830         { includes = val; }
2832     /**
2833      *
2834      */
2835     std::vector<String> getIncludes() const
2836         { return includes; }
2837         
2838     /**
2839      *
2840      */
2841     void setExcludes(const std::vector<String> &val)
2842         { excludes = val; }
2844     /**
2845      *
2846      */
2847     std::vector<String> getExcludes() const
2848         { return excludes; }
2849         
2850     /**
2851      *
2852      */
2853     unsigned int size() const
2854         { return files.size(); }
2855         
2856     /**
2857      *
2858      */
2859     String operator[](int index) const
2860         { return files[index]; }
2861         
2862     /**
2863      *
2864      */
2865     void clear()
2866         {
2867         directory = "";
2868         files.clear();
2869         includes.clear();
2870         excludes.clear();
2871         }
2872         
2874 private:
2876     void assign(const FileSet &other)
2877         {
2878         directory = other.directory;
2879         files     = other.files;
2880         includes  = other.includes;
2881         excludes  = other.excludes;
2882         }
2884     String directory;
2885     std::vector<String> files;
2886     std::vector<String> includes;
2887     std::vector<String> excludes;
2888 };
2891 //########################################################################
2892 //# F I L E L I S T
2893 //########################################################################
2894 /**
2895  * This is a simpler, explicitly-named list of files
2896  */
2897 class FileList
2899 public:
2901     /**
2902      *
2903      */
2904     FileList()
2905         {}
2907     /**
2908      *
2909      */
2910     FileList(const FileList &other)
2911         { assign(other); }
2913     /**
2914      *
2915      */
2916     FileList &operator=(const FileList &other)
2917         { assign(other); return *this; }
2919     /**
2920      *
2921      */
2922     virtual ~FileList()
2923         {}
2925     /**
2926      *
2927      */
2928     String getDirectory()
2929         { return directory; }
2930         
2931     /**
2932      *
2933      */
2934     void setDirectory(const String &val)
2935         { directory = val; }
2937     /**
2938      *
2939      */
2940     void setFiles(const std::vector<String> &val)
2941         { files = val; }
2943     /**
2944      *
2945      */
2946     std::vector<String> getFiles()
2947         { return files; }
2948         
2949     /**
2950      *
2951      */
2952     unsigned int size()
2953         { return files.size(); }
2954         
2955     /**
2956      *
2957      */
2958     String operator[](int index)
2959         { return files[index]; }
2960         
2961     /**
2962      *
2963      */
2964     void clear()
2965         {
2966         directory = "";
2967         files.clear();
2968         }
2969         
2971 private:
2973     void assign(const FileList &other)
2974         {
2975         directory = other.directory;
2976         files     = other.files;
2977         }
2979     String directory;
2980     std::vector<String> files;
2981 };
2986 //########################################################################
2987 //# M A K E    B A S E
2988 //########################################################################
2989 /**
2990  * Base class for all classes in this file
2991  */
2992 class MakeBase
2994 public:
2996     MakeBase()
2997         { line = 0; }
2998     virtual ~MakeBase()
2999         {}
3001     /**
3002      *     Return the URI of the file associated with this object 
3003      */     
3004     URI getURI()
3005         { return uri; }
3007     /**
3008      * Set the uri to the given string
3009      */
3010     void setURI(const String &uristr)
3011         { uri.parse(uristr); }
3013     /**
3014      *  Resolve another path relative to this one
3015      */
3016     String resolve(const String &otherPath);
3018     /**
3019      * replace variable refs like ${a} with their values
3020      * Assume that the string has already been syntax validated
3021      */
3022     String eval(const String &s, const String &defaultVal);
3024     /**
3025      * replace variable refs like ${a} with their values
3026      * return true or false
3027      * Assume that the string has already been syntax validated
3028      */
3029     bool evalBool(const String &s, bool defaultVal);
3031     /**
3032      *  Get an element attribute, performing substitutions if necessary
3033      */
3034     bool getAttribute(Element *elem, const String &name, String &result);
3036     /**
3037      * Get an element value, performing substitutions if necessary
3038      */
3039     bool getValue(Element *elem, String &result);
3040     
3041     /**
3042      * Set the current line number in the file
3043      */         
3044     void setLine(int val)
3045         { line = val; }
3046         
3047     /**
3048      * Get the current line number in the file
3049      */         
3050     int getLine()
3051         { return line; }
3054     /**
3055      * Set a property to a given value
3056      */
3057     virtual void setProperty(const String &name, const String &val)
3058         {
3059         properties[name] = val;
3060         }
3062     /**
3063      * Return a named property is found, else a null string
3064      */
3065     virtual String getProperty(const String &name)
3066         {
3067         String val;
3068         std::map<String, String>::iterator iter = properties.find(name);
3069         if (iter != properties.end())
3070             val = iter->second;
3071         String sval;
3072         if (!getSubstitutions(val, sval))
3073             return false;
3074         return sval;
3075         }
3077     /**
3078      * Return true if a named property is found, else false
3079      */
3080     virtual bool hasProperty(const String &name)
3081         {
3082         std::map<String, String>::iterator iter = properties.find(name);
3083         if (iter == properties.end())
3084             return false;
3085         return true;
3086         }
3089 protected:
3091     /**
3092      *    The path to the file associated with this object
3093      */     
3094     URI uri;
3095     
3096     /**
3097      *    If this prefix is seen in a substitution, use an environment
3098      *    variable.
3099      *             example:  <property environment="env"/>
3100      *             ${env.JAVA_HOME}
3101      */
3102     String envPrefix;
3104     /**
3105      *    If this prefix is seen in a substitution, use as a
3106      *    pkg-config 'all' query
3107      *             example:  <property pkg-config="pc"/>
3108      *             ${pc.gtkmm}
3109      */
3110     String pcPrefix;
3112     /**
3113      *    If this prefix is seen in a substitution, use as a
3114      *    pkg-config 'cflags' query
3115      *             example:  <property pkg-config="pcc"/>
3116      *             ${pcc.gtkmm}
3117      */
3118     String pccPrefix;
3120     /**
3121      *    If this prefix is seen in a substitution, use as a
3122      *    pkg-config 'libs' query
3123      *             example:  <property pkg-config="pcl"/>
3124      *             ${pcl.gtkmm}
3125      */
3126     String pclPrefix;
3132     /**
3133      *  Print a printf()-like formatted error message
3134      */
3135     void error(const char *fmt, ...);
3137     /**
3138      *  Print a printf()-like formatted trace message
3139      */
3140     void status(const char *fmt, ...);
3142     /**
3143      *  Show target status
3144      */
3145     void targetstatus(const char *fmt, ...);
3147     /**
3148      *  Print a printf()-like formatted trace message
3149      */
3150     void trace(const char *fmt, ...);
3152     /**
3153      *  Check if a given string matches a given regex pattern
3154      */
3155     bool regexMatch(const String &str, const String &pattern);
3157     /**
3158      *
3159      */
3160     String getSuffix(const String &fname);
3162     /**
3163      * Break up a string into substrings delimited the characters
3164      * in delimiters.  Null-length substrings are ignored
3165      */  
3166     std::vector<String> tokenize(const String &val,
3167                           const String &delimiters);
3169     /**
3170      *  replace runs of whitespace with a space
3171      */
3172     String strip(const String &s);
3174     /**
3175      *  remove leading whitespace from each line
3176      */
3177     String leftJustify(const String &s);
3179     /**
3180      *  remove leading and trailing whitespace from string
3181      */
3182     String trim(const String &s);
3184     /**
3185      *  Return a lower case version of the given string
3186      */
3187     String toLower(const String &s);
3189     /**
3190      * Return the native format of the canonical
3191      * path which we store
3192      */
3193     String getNativePath(const String &path);
3195     /**
3196      * Execute a shell command.  Outbuf is a ref to a string
3197      * to catch the result.     
3198      */         
3199     bool executeCommand(const String &call,
3200                         const String &inbuf,
3201                         String &outbuf,
3202                         String &errbuf);
3203     /**
3204      * List all directories in a given base and starting directory
3205      * It is usually called like:
3206      *        bool ret = listDirectories("src", "", result);    
3207      */         
3208     bool listDirectories(const String &baseName,
3209                          const String &dirname,
3210                          std::vector<String> &res);
3212     /**
3213      * Find all files in the named directory 
3214      */         
3215     bool listFiles(const String &baseName,
3216                    const String &dirname,
3217                    std::vector<String> &result);
3219     /**
3220      * Perform a listing for a fileset 
3221      */         
3222     bool listFiles(MakeBase &propRef, FileSet &fileSet);
3224     /**
3225      * Parse a <patternset>
3226      */  
3227     bool parsePatternSet(Element *elem,
3228                        MakeBase &propRef,
3229                        std::vector<String> &includes,
3230                        std::vector<String> &excludes);
3232     /**
3233      * Parse a <fileset> entry, and determine which files
3234      * should be included
3235      */  
3236     bool parseFileSet(Element *elem,
3237                     MakeBase &propRef,
3238                     FileSet &fileSet);
3239     /**
3240      * Parse a <filelist> entry
3241      */  
3242     bool parseFileList(Element *elem,
3243                     MakeBase &propRef,
3244                     FileList &fileList);
3246     /**
3247      * Return this object's property list
3248      */
3249     virtual std::map<String, String> &getProperties()
3250         { return properties; }
3253     std::map<String, String> properties;
3255     /**
3256      * Create a directory, making intermediate dirs
3257      * if necessary
3258      */                  
3259     bool createDirectory(const String &dirname);
3261     /**
3262      * Delete a directory and its children if desired
3263      */
3264     bool removeDirectory(const String &dirName);
3266     /**
3267      * Copy a file from one name to another. Perform only if needed
3268      */ 
3269     bool copyFile(const String &srcFile, const String &destFile);
3271     /**
3272      * Delete a file
3273      */ 
3274     bool removeFile(const String &file);
3276     /**
3277      * Tests if the file exists
3278      */ 
3279     bool fileExists(const String &fileName);
3281     /**
3282      * Tests if the file exists and is a regular file
3283      */ 
3284     bool isRegularFile(const String &fileName);
3286     /**
3287      * Tests if the file exists and is a directory
3288      */ 
3289     bool isDirectory(const String &fileName);
3291     /**
3292      * Tests is the modification date of fileA is newer than fileB
3293      */ 
3294     bool isNewerThan(const String &fileA, const String &fileB);
3296 private:
3298     bool pkgConfigRecursive(const String packageName,
3299                             const String &path, 
3300                             const String &prefix, 
3301                             int query,
3302                             String &result,
3303                             std::set<String> &deplist);
3305     /**
3306      * utility method to query for "all", "cflags", or "libs" for this package and its
3307      * dependencies.  0, 1, 2
3308      */          
3309     bool pkgConfigQuery(const String &packageName, int query, String &result);
3311     /**
3312      * replace a variable ref like ${a} with a value
3313      */
3314     bool lookupProperty(const String &s, String &result);
3315     
3316     /**
3317      * called by getSubstitutions().  This is in case a looked-up string
3318      * has substitutions also.     
3319      */
3320     bool getSubstitutionsRecursive(const String &s, String &result, int depth);
3322     /**
3323      * replace variable refs in a string like ${a} with their values
3324      */
3325     bool getSubstitutions(const String &s, String &result);
3327     int line;
3330 };
3334 /**
3335  * Define the pkg-config class here, since it will be used in MakeBase method
3336  * implementations. 
3337  */
3338 class PkgConfig : public MakeBase
3341 public:
3343     /**
3344      *
3345      */
3346     PkgConfig()
3347         {
3348          path   = ".";
3349          prefix = "/target";
3350          init();
3351          }
3353     /**
3354      *
3355      */
3356     PkgConfig(const PkgConfig &other)
3357         { assign(other); }
3359     /**
3360      *
3361      */
3362     PkgConfig &operator=(const PkgConfig &other)
3363         { assign(other); return *this; }
3365     /**
3366      *
3367      */
3368     virtual ~PkgConfig()
3369         { }
3371     /**
3372      *
3373      */
3374     virtual String getName()
3375         { return name; }
3377     /**
3378      *
3379      */
3380     virtual String getPath()
3381         { return path; }
3383     /**
3384      *
3385      */
3386     virtual void setPath(const String &val)
3387         { path = val; }
3389     /**
3390      *
3391      */
3392     virtual String getPrefix()
3393         { return prefix; }
3395     /**
3396      *  Allow the user to override the prefix in the file
3397      */
3398     virtual void setPrefix(const String &val)
3399         { prefix = val; }
3401     /**
3402      *
3403      */
3404     virtual String getDescription()
3405         { return description; }
3407     /**
3408      *
3409      */
3410     virtual String getCflags()
3411         { return cflags; }
3413     /**
3414      *
3415      */
3416     virtual String getLibs()
3417         { return libs; }
3419     /**
3420      *
3421      */
3422     virtual String getAll()
3423         {
3424          String ret = cflags;
3425          ret.append(" ");
3426          ret.append(libs);
3427          return ret;
3428         }
3430     /**
3431      *
3432      */
3433     virtual String getVersion()
3434         { return version; }
3436     /**
3437      *
3438      */
3439     virtual int getMajorVersion()
3440         { return majorVersion; }
3442     /**
3443      *
3444      */
3445     virtual int getMinorVersion()
3446         { return minorVersion; }
3448     /**
3449      *
3450      */
3451     virtual int getMicroVersion()
3452         { return microVersion; }
3454     /**
3455      *
3456      */
3457     virtual std::map<String, String> &getAttributes()
3458         { return attrs; }
3460     /**
3461      *
3462      */
3463     virtual std::vector<String> &getRequireList()
3464         { return requireList; }
3466     /**
3467      *  Read a file for its details
3468      */         
3469     virtual bool readFile(const String &fileName);
3471     /**
3472      *  Read a file for its details
3473      */         
3474     virtual bool query(const String &name);
3476 private:
3478     void init()
3479         {
3480         //do not set path and prefix here
3481         name         = "";
3482         description  = "";
3483         cflags       = "";
3484         libs         = "";
3485         requires     = "";
3486         version      = "";
3487         majorVersion = 0;
3488         minorVersion = 0;
3489         microVersion = 0;
3490         fileName     = "";
3491         attrs.clear();
3492         requireList.clear();
3493         }
3495     void assign(const PkgConfig &other)
3496         {
3497         name         = other.name;
3498         path         = other.path;
3499         prefix       = other.prefix;
3500         description  = other.description;
3501         cflags       = other.cflags;
3502         libs         = other.libs;
3503         requires     = other.requires;
3504         version      = other.version;
3505         majorVersion = other.majorVersion;
3506         minorVersion = other.minorVersion;
3507         microVersion = other.microVersion;
3508         fileName     = other.fileName;
3509         attrs        = other.attrs;
3510         requireList  = other.requireList;
3511         }
3515     int get(int pos);
3517     int skipwhite(int pos);
3519     int getword(int pos, String &ret);
3521     /**
3522      * Very important
3523      */         
3524     bool parseRequires();
3526     void parseVersion();
3528     bool parseLine(const String &lineBuf);
3530     bool parse(const String &buf);
3532     void dumpAttrs();
3534     String name;
3536     String path;
3538     String prefix;
3540     String description;
3542     String cflags;
3544     String libs;
3546     String requires;
3548     String version;
3550     int majorVersion;
3552     int minorVersion;
3554     int microVersion;
3556     String fileName;
3558     std::map<String, String> attrs;
3560     std::vector<String> requireList;
3562     char *parsebuf;
3563     int parselen;
3564 };
3569 /**
3570  *  Print a printf()-like formatted error message
3571  */
3572 void MakeBase::error(const char *fmt, ...)
3574     va_list args;
3575     va_start(args,fmt);
3576     fprintf(stderr, "Make error line %d: ", line);
3577     vfprintf(stderr, fmt, args);
3578     fprintf(stderr, "\n");
3579     va_end(args) ;
3584 /**
3585  *  Print a printf()-like formatted trace message
3586  */
3587 void MakeBase::status(const char *fmt, ...)
3589     va_list args;
3590     //fprintf(stdout, " ");
3591     va_start(args,fmt);
3592     vfprintf(stdout, fmt, args);
3593     va_end(args);
3594     fprintf(stdout, "\n");
3595     fflush(stdout);
3599 /**
3600  *  Print a printf()-like formatted trace message
3601  */
3602 void MakeBase::trace(const char *fmt, ...)
3604     va_list args;
3605     fprintf(stdout, "Make: ");
3606     va_start(args,fmt);
3607     vfprintf(stdout, fmt, args);
3608     va_end(args) ;
3609     fprintf(stdout, "\n");
3610     fflush(stdout);
3615 /**
3616  *  Resolve another path relative to this one
3617  */
3618 String MakeBase::resolve(const String &otherPath)
3620     URI otherURI(otherPath);
3621     URI fullURI = uri.resolve(otherURI);
3622     String ret = fullURI.toString();
3623     return ret;
3628 /**
3629  *  Check if a given string matches a given regex pattern
3630  */
3631 bool MakeBase::regexMatch(const String &str, const String &pattern)
3633     const TRexChar *terror = NULL;
3634     const TRexChar *cpat = pattern.c_str();
3635     TRex *expr = trex_compile(cpat, &terror);
3636     if (!expr)
3637         {
3638         if (!terror)
3639             terror = "undefined";
3640         error("compilation error [%s]!\n", terror);
3641         return false;
3642         } 
3644     bool ret = true;
3646     const TRexChar *cstr = str.c_str();
3647     if (trex_match(expr, cstr))
3648         {
3649         ret = true;
3650         }
3651     else
3652         {
3653         ret = false;
3654         }
3656     trex_free(expr);
3658     return ret;
3661 /**
3662  *  Return the suffix, if any, of a file name
3663  */
3664 String MakeBase::getSuffix(const String &fname)
3666     if (fname.size() < 2)
3667         return "";
3668     unsigned int pos = fname.find_last_of('.');
3669     if (pos == fname.npos)
3670         return "";
3671     pos++;
3672     String res = fname.substr(pos, fname.size()-pos);
3673     //trace("suffix:%s", res.c_str()); 
3674     return res;
3679 /**
3680  * Break up a string into substrings delimited the characters
3681  * in delimiters.  Null-length substrings are ignored
3682  */  
3683 std::vector<String> MakeBase::tokenize(const String &str,
3684                                 const String &delimiters)
3687     std::vector<String> res;
3688     char *del = (char *)delimiters.c_str();
3689     String dmp;
3690     for (unsigned int i=0 ; i<str.size() ; i++)
3691         {
3692         char ch = str[i];
3693         char *p = (char *)0;
3694         for (p=del ; *p ; p++)
3695             if (*p == ch)
3696                 break;
3697         if (*p)
3698             {
3699             if (dmp.size() > 0)
3700                 {
3701                 res.push_back(dmp);
3702                 dmp.clear();
3703                 }
3704             }
3705         else
3706             {
3707             dmp.push_back(ch);
3708             }
3709         }
3710     //Add tail
3711     if (dmp.size() > 0)
3712         {
3713         res.push_back(dmp);
3714         dmp.clear();
3715         }
3717     return res;
3722 /**
3723  *  replace runs of whitespace with a single space
3724  */
3725 String MakeBase::strip(const String &s)
3727     int len = s.size();
3728     String stripped;
3729     for (int i = 0 ; i<len ; i++)
3730         {
3731         char ch = s[i];
3732         if (isspace(ch))
3733             {
3734             stripped.push_back(' ');
3735             for ( ; i<len ; i++)
3736                 {
3737                 ch = s[i];
3738                 if (!isspace(ch))
3739                     {
3740                     stripped.push_back(ch);
3741                     break;
3742                     }
3743                 }
3744             }
3745         else
3746             {
3747             stripped.push_back(ch);
3748             }
3749         }
3750     return stripped;
3753 /**
3754  *  remove leading whitespace from each line
3755  */
3756 String MakeBase::leftJustify(const String &s)
3758     String out;
3759     int len = s.size();
3760     for (int i = 0 ; i<len ; )
3761         {
3762         char ch;
3763         //Skip to first visible character
3764         while (i<len)
3765             {
3766             ch = s[i];
3767             if (ch == '\n' || ch == '\r'
3768               || !isspace(ch))
3769                   break;
3770             i++;
3771             }
3772         //Copy the rest of the line
3773         while (i<len)
3774             {
3775             ch = s[i];
3776             if (ch == '\n' || ch == '\r')
3777                 {
3778                 if (ch != '\r')
3779                     out.push_back('\n');
3780                 i++;
3781                 break;
3782                 }
3783             else
3784                 {
3785                 out.push_back(ch);
3786                 }
3787             i++;
3788             }
3789         }
3790     return out;
3794 /**
3795  *  Removes whitespace from beginning and end of a string
3796  */
3797 String MakeBase::trim(const String &s)
3799     if (s.size() < 1)
3800         return s;
3801     
3802     //Find first non-ws char
3803     unsigned int begin = 0;
3804     for ( ; begin < s.size() ; begin++)
3805         {
3806         if (!isspace(s[begin]))
3807             break;
3808         }
3810     //Find first non-ws char, going in reverse
3811     unsigned int end = s.size() - 1;
3812     for ( ; end > begin ; end--)
3813         {
3814         if (!isspace(s[end]))
3815             break;
3816         }
3817     //trace("begin:%d  end:%d", begin, end);
3819     String res = s.substr(begin, end-begin+1);
3820     return res;
3824 /**
3825  *  Return a lower case version of the given string
3826  */
3827 String MakeBase::toLower(const String &s)
3829     if (s.size()==0)
3830         return s;
3832     String ret;
3833     for(unsigned int i=0; i<s.size() ; i++)
3834         {
3835         ret.push_back(tolower(s[i]));
3836         }
3837     return ret;
3841 /**
3842  * Return the native format of the canonical
3843  * path which we store
3844  */
3845 String MakeBase::getNativePath(const String &path)
3847 #ifdef __WIN32__
3848     String npath;
3849     unsigned int firstChar = 0;
3850     if (path.size() >= 3)
3851         {
3852         if (path[0] == '/' &&
3853             isalpha(path[1]) &&
3854             path[2] == ':')
3855             firstChar++;
3856         }
3857     for (unsigned int i=firstChar ; i<path.size() ; i++)
3858         {
3859         char ch = path[i];
3860         if (ch == '/')
3861             npath.push_back('\\');
3862         else
3863             npath.push_back(ch);
3864         }
3865     return npath;
3866 #else
3867     return path;
3868 #endif
3872 #ifdef __WIN32__
3873 #include <tchar.h>
3875 static String win32LastError()
3878     DWORD dw = GetLastError(); 
3880     LPVOID str;
3881     FormatMessage(
3882         FORMAT_MESSAGE_ALLOCATE_BUFFER | 
3883         FORMAT_MESSAGE_FROM_SYSTEM,
3884         NULL,
3885         dw,
3886         0,
3887         (LPTSTR) &str,
3888         0, NULL );
3889     LPTSTR p = _tcschr((const char *)str, _T('\r'));
3890     if(p != NULL)
3891         { // lose CRLF
3892         *p = _T('\0');
3893         }
3894     String ret = (char *)str;
3895     LocalFree(str);
3897     return ret;
3899 #endif
3904 #ifdef __WIN32__
3906 /**
3907  * Execute a system call, using pipes to send data to the
3908  * program's stdin,  and reading stdout and stderr.
3909  */
3910 bool MakeBase::executeCommand(const String &command,
3911                               const String &inbuf,
3912                               String &outbuf,
3913                               String &errbuf)
3916     status("============ cmd ============\n%s\n=============================",
3917                 command.c_str());
3919     outbuf.clear();
3920     errbuf.clear();
3921     
3923     /*
3924     I really hate having win32 code in this program, but the
3925     read buffer in command.com and cmd.exe are just too small
3926     for the large commands we need for compiling and linking.
3927     */
3929     bool ret = true;
3931     //# Allocate a separate buffer for safety
3932     char *paramBuf = new char[command.size() + 1];
3933     if (!paramBuf)
3934        {
3935        error("executeCommand cannot allocate command buffer");
3936        return false;
3937        }
3938     strcpy(paramBuf, (char *)command.c_str());
3940     //# Go to http://msdn2.microsoft.com/en-us/library/ms682499.aspx
3941     //# to see how Win32 pipes work
3943     //# Create pipes
3944     SECURITY_ATTRIBUTES saAttr; 
3945     saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); 
3946     saAttr.bInheritHandle = TRUE; 
3947     saAttr.lpSecurityDescriptor = NULL; 
3948     HANDLE stdinRead,  stdinWrite;
3949     HANDLE stdoutRead, stdoutWrite;
3950     HANDLE stderrRead, stderrWrite;
3951     if (!CreatePipe(&stdinRead, &stdinWrite, &saAttr, 0))
3952         {
3953         error("executeProgram: could not create pipe");
3954         delete[] paramBuf;
3955         return false;
3956         } 
3957     SetHandleInformation(stdinWrite, HANDLE_FLAG_INHERIT, 0);
3958     if (!CreatePipe(&stdoutRead, &stdoutWrite, &saAttr, 0))
3959         {
3960         error("executeProgram: could not create pipe");
3961         delete[] paramBuf;
3962         return false;
3963         } 
3964     SetHandleInformation(stdoutRead, HANDLE_FLAG_INHERIT, 0);
3965     if (!CreatePipe(&stderrRead, &stderrWrite, &saAttr, 0))
3966         {
3967         error("executeProgram: could not create pipe");
3968         delete[] paramBuf;
3969         return false;
3970         } 
3971     SetHandleInformation(stderrRead, HANDLE_FLAG_INHERIT, 0);
3973     // Create the process
3974     STARTUPINFO siStartupInfo;
3975     PROCESS_INFORMATION piProcessInfo;
3976     memset(&siStartupInfo, 0, sizeof(siStartupInfo));
3977     memset(&piProcessInfo, 0, sizeof(piProcessInfo));
3978     siStartupInfo.cb = sizeof(siStartupInfo);
3979     siStartupInfo.hStdError   =  stderrWrite;
3980     siStartupInfo.hStdOutput  =  stdoutWrite;
3981     siStartupInfo.hStdInput   =  stdinRead;
3982     siStartupInfo.dwFlags    |=  STARTF_USESTDHANDLES;
3983    
3984     if (!CreateProcess(NULL, paramBuf, NULL, NULL, true,
3985                 0, NULL, NULL, &siStartupInfo,
3986                 &piProcessInfo))
3987         {
3988         error("executeCommand : could not create process : %s",
3989                     win32LastError().c_str());
3990         ret = false;
3991         }
3993     delete[] paramBuf;
3995     DWORD bytesWritten;
3996     if (inbuf.size()>0 &&
3997         !WriteFile(stdinWrite, inbuf.c_str(), inbuf.size(), 
3998                &bytesWritten, NULL))
3999         {
4000         error("executeCommand: could not write to pipe");
4001         return false;
4002         }    
4003     if (!CloseHandle(stdinWrite))
4004         {          
4005         error("executeCommand: could not close write pipe");
4006         return false;
4007         }
4008     if (!CloseHandle(stdoutWrite))
4009         {
4010         error("executeCommand: could not close read pipe");
4011         return false;
4012         }
4013     if (!CloseHandle(stderrWrite))
4014         {
4015         error("executeCommand: could not close read pipe");
4016         return false;
4017         }
4019     bool lastLoop = false;
4020     while (true)
4021         {
4022         DWORD avail;
4023         DWORD bytesRead;
4024         char readBuf[4096];
4026         //trace("## stderr");
4027         PeekNamedPipe(stderrRead, NULL, 0, NULL, &avail, NULL);
4028         if (avail > 0)
4029             {
4030             bytesRead = 0;
4031             if (avail>4096) avail = 4096;
4032             ReadFile(stderrRead, readBuf, avail, &bytesRead, NULL);
4033             if (bytesRead > 0)
4034                 {
4035                 for (unsigned int i=0 ; i<bytesRead ; i++)
4036                     errbuf.push_back(readBuf[i]);
4037                 }
4038             }
4040         //trace("## stdout");
4041         PeekNamedPipe(stdoutRead, NULL, 0, NULL, &avail, NULL);
4042         if (avail > 0)
4043             {
4044             bytesRead = 0;
4045             if (avail>4096) avail = 4096;
4046             ReadFile(stdoutRead, readBuf, avail, &bytesRead, NULL);
4047             if (bytesRead > 0)
4048                 {
4049                 for (unsigned int i=0 ; i<bytesRead ; i++)
4050                     outbuf.push_back(readBuf[i]);
4051                 }
4052             }
4053             
4054         //Was this the final check after program done?
4055         if (lastLoop)
4056             break;
4058         DWORD exitCode;
4059         GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
4060         if (exitCode != STILL_ACTIVE)
4061             lastLoop = true;
4063         Sleep(10);
4064         }    
4065     //trace("outbuf:%s", outbuf.c_str());
4066     if (!CloseHandle(stdoutRead))
4067         {
4068         error("executeCommand: could not close read pipe");
4069         return false;
4070         }
4071     if (!CloseHandle(stderrRead))
4072         {
4073         error("executeCommand: could not close read pipe");
4074         return false;
4075         }
4077     DWORD exitCode;
4078     GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
4079     //trace("exit code:%d", exitCode);
4080     if (exitCode != 0)
4081         {
4082         ret = false;
4083         }
4084     
4085     CloseHandle(piProcessInfo.hProcess);
4086     CloseHandle(piProcessInfo.hThread);
4088     return ret;
4090
4092 #else  /*do it unix style*/
4094 #include <sys/wait.h>
4098 /**
4099  * Execute a system call, using pipes to send data to the
4100  * program's stdin,  and reading stdout and stderr.
4101  */
4102 bool MakeBase::executeCommand(const String &command,
4103                               const String &inbuf,
4104                               String &outbuf,
4105                               String &errbuf)
4108     status("============ cmd ============\n%s\n=============================",
4109                 command.c_str());
4111     outbuf.clear();
4112     errbuf.clear();
4113     
4115     int outfds[2];
4116     if (pipe(outfds) < 0)
4117         return false;
4118     int errfds[2];
4119     if (pipe(errfds) < 0)
4120         return false;
4121     int pid = fork();
4122     if (pid < 0)
4123         {
4124         close(outfds[0]);
4125         close(outfds[1]);
4126         close(errfds[0]);
4127         close(errfds[1]);
4128         error("launch of command '%s' failed : %s",
4129              command.c_str(), strerror(errno));
4130         return false;
4131         }
4132     else if (pid > 0) // parent
4133         {
4134         close(outfds[1]);
4135         close(errfds[1]);
4136         }
4137     else // == 0, child
4138         {
4139         close(outfds[0]);
4140         dup2(outfds[1], STDOUT_FILENO);
4141         close(outfds[1]);
4142         close(errfds[0]);
4143         dup2(errfds[1], STDERR_FILENO);
4144         close(errfds[1]);
4146         char *args[4];
4147         args[0] = (char *)"sh";
4148         args[1] = (char *)"-c";
4149         args[2] = (char *)command.c_str();
4150         args[3] = NULL;
4151         execv("/bin/sh", args);
4152         exit(EXIT_FAILURE);
4153         }
4155     String outb;
4156     String errb;
4158     int outRead = outfds[0];
4159     int errRead = errfds[0];
4160     int max = outRead;
4161     if (errRead > max)
4162         max = errRead;
4164     bool outOpen = true;
4165     bool errOpen = true;
4167     while (outOpen || errOpen)
4168         {
4169         char ch;
4170         fd_set fdset;
4171         FD_ZERO(&fdset);
4172         if (outOpen)
4173             FD_SET(outRead, &fdset);
4174         if (errOpen)
4175             FD_SET(errRead, &fdset);
4176         int ret = select(max+1, &fdset, NULL, NULL, NULL);
4177         if (ret < 0)
4178             break;
4179         if (FD_ISSET(outRead, &fdset))
4180             {
4181             if (read(outRead, &ch, 1) <= 0)
4182                 { outOpen = false; }
4183             else if (ch <= 0)
4184                 { /* outOpen = false; */ }
4185             else
4186                 { outb.push_back(ch); }
4187             }
4188         if (FD_ISSET(errRead, &fdset))
4189             {
4190             if (read(errRead, &ch, 1) <= 0)
4191                 { errOpen = false; }
4192             else if (ch <= 0)
4193                 { /* errOpen = false; */ }
4194             else
4195                 { errb.push_back(ch); }
4196             }
4197         }
4199     int childReturnValue;
4200     wait(&childReturnValue);
4202     close(outRead);
4203     close(errRead);
4205     outbuf = outb;
4206     errbuf = errb;
4208     if (childReturnValue != 0)
4209         {
4210         error("exec of command '%s' failed : %s",
4211              command.c_str(), strerror(childReturnValue));
4212         return false;
4213         }
4215     return true;
4216
4218 #endif
4223 bool MakeBase::listDirectories(const String &baseName,
4224                               const String &dirName,
4225                               std::vector<String> &res)
4227     res.push_back(dirName);
4228     String fullPath = baseName;
4229     if (dirName.size()>0)
4230         {
4231         if (dirName[0]!='/') fullPath.append("/");
4232         fullPath.append(dirName);
4233         }
4234     DIR *dir = opendir(fullPath.c_str());
4235     while (true)
4236         {
4237         struct dirent *de = readdir(dir);
4238         if (!de)
4239             break;
4241         //Get the directory member name
4242         String s = de->d_name;
4243         if (s.size() == 0 || s[0] == '.')
4244             continue;
4245         String childName = dirName;
4246         childName.append("/");
4247         childName.append(s);
4249         String fullChildPath = baseName;
4250         fullChildPath.append("/");
4251         fullChildPath.append(childName);
4252         struct stat finfo;
4253         String childNative = getNativePath(fullChildPath);
4254         if (cachedStat(childNative, &finfo)<0)
4255             {
4256             error("cannot stat file:%s", childNative.c_str());
4257             }
4258         else if (S_ISDIR(finfo.st_mode))
4259             {
4260             //trace("directory: %s", childName.c_str());
4261             if (!listDirectories(baseName, childName, res))
4262                 return false;
4263             }
4264         }
4265     closedir(dir);
4267     return true;
4271 bool MakeBase::listFiles(const String &baseDir,
4272                          const String &dirName,
4273                          std::vector<String> &res)
4275     String fullDir = baseDir;
4276     if (dirName.size()>0)
4277         {
4278         fullDir.append("/");
4279         fullDir.append(dirName);
4280         }
4281     String dirNative = getNativePath(fullDir);
4283     std::vector<String> subdirs;
4284     DIR *dir = opendir(dirNative.c_str());
4285     if (!dir)
4286         {
4287         error("Could not open directory %s : %s",
4288               dirNative.c_str(), strerror(errno));
4289         return false;
4290         }
4291     while (true)
4292         {
4293         struct dirent *de = readdir(dir);
4294         if (!de)
4295             break;
4297         //Get the directory member name
4298         String s = de->d_name;
4299         if (s.size() == 0 || s[0] == '.')
4300             continue;
4301         String childName;
4302         if (dirName.size()>0)
4303             {
4304             childName.append(dirName);
4305             childName.append("/");
4306             }
4307         childName.append(s);
4308         String fullChild = baseDir;
4309         fullChild.append("/");
4310         fullChild.append(childName);
4311         
4312         if (isDirectory(fullChild))
4313             {
4314             //trace("directory: %s", childName.c_str());
4315             if (!listFiles(baseDir, childName, res))
4316                 return false;
4317             continue;
4318             }
4319         else if (!isRegularFile(fullChild))
4320             {
4321             error("unknown file:%s", childName.c_str());
4322             return false;
4323             }
4325        //all done!
4326         res.push_back(childName);
4328         }
4329     closedir(dir);
4331     return true;
4335 /**
4336  * Several different classes extend MakeBase.  By "propRef", we mean
4337  * the one holding the properties.  Likely "Make" itself
4338  */
4339 bool MakeBase::listFiles(MakeBase &propRef, FileSet &fileSet)
4341     //before doing the list,  resolve any property references
4342     //that might have been specified in the directory name, such as ${src}
4343     String fsDir = fileSet.getDirectory();
4344     String dir;
4345     if (!propRef.getSubstitutions(fsDir, dir))
4346         return false;
4347     String baseDir = propRef.resolve(dir);
4348     std::vector<String> fileList;
4349     if (!listFiles(baseDir, "", fileList))
4350         return false;
4352     std::vector<String> includes = fileSet.getIncludes();
4353     std::vector<String> excludes = fileSet.getExcludes();
4355     std::vector<String> incs;
4356     std::vector<String>::iterator iter;
4358     std::sort(fileList.begin(), fileList.end());
4360     //If there are <includes>, then add files to the output
4361     //in the order of the include list
4362     if (includes.size()==0)
4363         incs = fileList;
4364     else
4365         {
4366         for (iter = includes.begin() ; iter != includes.end() ; iter++)
4367             {
4368             String &pattern = *iter;
4369             std::vector<String>::iterator siter;
4370             for (siter = fileList.begin() ; siter != fileList.end() ; siter++)
4371                 {
4372                 String s = *siter;
4373                 if (regexMatch(s, pattern))
4374                     {
4375                     //trace("INCLUDED:%s", s.c_str());
4376                     incs.push_back(s);
4377                     }
4378                 }
4379             }
4380         }
4382     //Now trim off the <excludes>
4383     std::vector<String> res;
4384     for (iter = incs.begin() ; iter != incs.end() ; iter++)
4385         {
4386         String s = *iter;
4387         bool skipme = false;
4388         std::vector<String>::iterator siter;
4389         for (siter = excludes.begin() ; siter != excludes.end() ; siter++)
4390             {
4391             String &pattern = *siter;
4392             if (regexMatch(s, pattern))
4393                 {
4394                 //trace("EXCLUDED:%s", s.c_str());
4395                 skipme = true;
4396                 break;
4397                 }
4398             }
4399         if (!skipme)
4400             res.push_back(s);
4401         }
4402         
4403     fileSet.setFiles(res);
4405     return true;
4409 /**
4410  * 0 == all, 1 = cflags, 2 = libs
4411  */ 
4412 bool MakeBase::pkgConfigRecursive(const String packageName,
4413                                   const String &path, 
4414                                   const String &prefix, 
4415                                   int query,
4416                                   String &result,
4417                                   std::set<String> &deplist) 
4419     PkgConfig pkgConfig;
4420     if (path.size() > 0)
4421         pkgConfig.setPath(path);
4422     if (prefix.size() > 0)
4423         pkgConfig.setPrefix(prefix);
4424     if (!pkgConfig.query(packageName))
4425         return false;
4426     if (query == 0)
4427         result = pkgConfig.getAll();
4428     else if (query == 1)
4429         result = pkgConfig.getCflags();
4430     else
4431         result = pkgConfig.getLibs();
4432     deplist.insert(packageName);
4433     std::vector<String> list = pkgConfig.getRequireList();
4434     for (unsigned int i = 0 ; i<list.size() ; i++)
4435         {
4436         String depPkgName = list[i];
4437         if (deplist.find(depPkgName) != deplist.end())
4438             continue;
4439         String val;
4440         if (!pkgConfigRecursive(depPkgName, path, prefix, query, val, deplist))
4441             {
4442             error("Based on 'requires' attribute of package '%s'", packageName.c_str());
4443             return false;
4444             }
4445         result.append(" ");
4446         result.append(val);
4447         }
4449     return true;
4452 bool MakeBase::pkgConfigQuery(const String &packageName, int query, String &result)
4454     std::set<String> deplist;
4455     String path = getProperty("pkg-config-path");
4456     if (path.size()>0)
4457         path = resolve(path);
4458     String prefix = getProperty("pkg-config-prefix");
4459     String val;
4460     if (!pkgConfigRecursive(packageName, path, prefix, query, val, deplist))
4461         return false;
4462     result = val;
4463     return true;
4468 /**
4469  * replace a variable ref like ${a} with a value
4470  */
4471 bool MakeBase::lookupProperty(const String &propertyName, String &result)
4473     String varname = propertyName;
4474     if (envPrefix.size() > 0 &&
4475         varname.compare(0, envPrefix.size(), envPrefix) == 0)
4476         {
4477         varname = varname.substr(envPrefix.size());
4478         char *envstr = getenv(varname.c_str());
4479         if (!envstr)
4480             {
4481             error("environment variable '%s' not defined", varname.c_str());
4482             return false;
4483             }
4484         result = envstr;
4485         }
4486     else if (pcPrefix.size() > 0 &&
4487         varname.compare(0, pcPrefix.size(), pcPrefix) == 0)
4488         {
4489         varname = varname.substr(pcPrefix.size());
4490         String val;
4491         if (!pkgConfigQuery(varname, 0, val))
4492             return false;
4493         result = val;
4494         }
4495     else if (pccPrefix.size() > 0 &&
4496         varname.compare(0, pccPrefix.size(), pccPrefix) == 0)
4497         {
4498         varname = varname.substr(pccPrefix.size());
4499         String val;
4500         if (!pkgConfigQuery(varname, 1, val))
4501             return false;
4502         result = val;
4503         }
4504     else if (pclPrefix.size() > 0 &&
4505         varname.compare(0, pclPrefix.size(), pclPrefix) == 0)
4506         {
4507         varname = varname.substr(pclPrefix.size());
4508         String val;
4509         if (!pkgConfigQuery(varname, 2, val))
4510             return false;
4511         result = val;
4512         }
4513     else
4514         {
4515         std::map<String, String>::iterator iter;
4516         iter = properties.find(varname);
4517         if (iter != properties.end())
4518             {
4519             result = iter->second;
4520             }
4521         else
4522             {
4523             error("property '%s' not found", varname.c_str());
4524             return false;
4525             }
4526         }
4527     return true;
4533 /**
4534  * Analyse a string, looking for any substitutions or other
4535  * things that need resolution 
4536  */
4537 bool MakeBase::getSubstitutionsRecursive(const String &str,
4538                                          String &result, int depth)
4540     if (depth > 10)
4541         {
4542         error("nesting of substitutions too deep (>10) for '%s'",
4543                         str.c_str());
4544         return false;
4545         }
4546     String s = trim(str);
4547     int len = (int)s.size();
4548     String val;
4549     for (int i=0 ; i<len ; i++)
4550         {
4551         char ch = s[i];
4552         if (ch == '$' && s[i+1] == '{')
4553             {
4554             String varname;
4555             int j = i+2;
4556             for ( ; j<len ; j++)
4557                 {
4558                 ch = s[j];
4559                 if (ch == '$' && s[j+1] == '{')
4560                     {
4561                     error("attribute %s cannot have nested variable references",
4562                            s.c_str());
4563                     return false;
4564                     }
4565                 else if (ch == '}')
4566                     {
4567                     varname = trim(varname);
4568                     String varval;
4569                     if (!lookupProperty(varname, varval))
4570                         return false;
4571                     String varval2;
4572                     //Now see if the answer has ${} in it, too
4573                     if (!getSubstitutionsRecursive(varval, varval2, depth + 1))
4574                         return false;
4575                     val.append(varval2);
4576                     break;
4577                     }
4578                 else
4579                     {
4580                     varname.push_back(ch);
4581                     }
4582                 }
4583             i = j;
4584             }
4585         else
4586             {
4587             val.push_back(ch);
4588             }
4589         }
4590     result = val;
4591     return true;
4594 /**
4595  * Analyse a string, looking for any substitutions or other
4596  * things that need resilution 
4597  */
4598 bool MakeBase::getSubstitutions(const String &str, String &result)
4600     return getSubstitutionsRecursive(str, result, 0);
4605 /**
4606  * replace variable refs like ${a} with their values
4607  * Assume that the string has already been syntax validated
4608  */
4609 String MakeBase::eval(const String &s, const String &defaultVal)
4611     if (s.size()==0)
4612         return defaultVal;
4613     String ret;
4614     if (getSubstitutions(s, ret))
4615         return ret;
4616     else
4617         return defaultVal;
4621 /**
4622  * replace variable refs like ${a} with their values
4623  * return true or false
4624  * Assume that the string has already been syntax validated
4625  */
4626 bool MakeBase::evalBool(const String &s, bool defaultVal)
4628     if (s.size()==0)
4629         return defaultVal;
4630     String val = eval(s, "false");
4631     if (val.size()==0)
4632         return defaultVal;
4633     if (val == "true" || val == "TRUE")
4634         return true;
4635     else
4636         return false;
4640 /**
4641  * Get a string attribute, testing it for proper syntax and
4642  * property names.
4643  */
4644 bool MakeBase::getAttribute(Element *elem, const String &name,
4645                                     String &result)
4647     String s = elem->getAttribute(name);
4648     String tmp;
4649     bool ret = getSubstitutions(s, tmp);
4650     if (ret)
4651         result = s;  //assign -if- ok
4652     return ret;
4656 /**
4657  * Get a string value, testing it for proper syntax and
4658  * property names.
4659  */
4660 bool MakeBase::getValue(Element *elem, String &result)
4662     String s = elem->getValue();
4663     String tmp;
4664     bool ret = getSubstitutions(s, tmp);
4665     if (ret)
4666         result = s;  //assign -if- ok
4667     return ret;
4673 /**
4674  * Parse a <patternset> entry
4675  */  
4676 bool MakeBase::parsePatternSet(Element *elem,
4677                           MakeBase &propRef,
4678                           std::vector<String> &includes,
4679                           std::vector<String> &excludes
4680                           )
4682     std::vector<Element *> children  = elem->getChildren();
4683     for (unsigned int i=0 ; i<children.size() ; i++)
4684         {
4685         Element *child = children[i];
4686         String tagName = child->getName();
4687         if (tagName == "exclude")
4688             {
4689             String fname;
4690             if (!propRef.getAttribute(child, "name", fname))
4691                 return false;
4692             //trace("EXCLUDE: %s", fname.c_str());
4693             excludes.push_back(fname);
4694             }
4695         else if (tagName == "include")
4696             {
4697             String fname;
4698             if (!propRef.getAttribute(child, "name", fname))
4699                 return false;
4700             //trace("INCLUDE: %s", fname.c_str());
4701             includes.push_back(fname);
4702             }
4703         }
4705     return true;
4711 /**
4712  * Parse a <fileset> entry, and determine which files
4713  * should be included
4714  */  
4715 bool MakeBase::parseFileSet(Element *elem,
4716                           MakeBase &propRef,
4717                           FileSet &fileSet)
4719     String name = elem->getName();
4720     if (name != "fileset")
4721         {
4722         error("expected <fileset>");
4723         return false;
4724         }
4727     std::vector<String> includes;
4728     std::vector<String> excludes;
4730     //A fileset has one implied patternset
4731     if (!parsePatternSet(elem, propRef, includes, excludes))
4732         {
4733         return false;
4734         }
4735     //Look for child tags, including more patternsets
4736     std::vector<Element *> children  = elem->getChildren();
4737     for (unsigned int i=0 ; i<children.size() ; i++)
4738         {
4739         Element *child = children[i];
4740         String tagName = child->getName();
4741         if (tagName == "patternset")
4742             {
4743             if (!parsePatternSet(child, propRef, includes, excludes))
4744                 {
4745                 return false;
4746                 }
4747             }
4748         }
4750     String dir;
4751     //Now do the stuff
4752     //Get the base directory for reading file names
4753     if (!propRef.getAttribute(elem, "dir", dir))
4754         return false;
4756     fileSet.setDirectory(dir);
4757     fileSet.setIncludes(includes);
4758     fileSet.setExcludes(excludes);
4759     
4760     /*
4761     std::vector<String> fileList;
4762     if (dir.size() > 0)
4763         {
4764         String baseDir = propRef.resolve(dir);
4765         if (!listFiles(baseDir, "", includes, excludes, fileList))
4766             return false;
4767         }
4768     std::sort(fileList.begin(), fileList.end());
4769     result = fileList;
4770     */
4772     
4773     /*
4774     for (unsigned int i=0 ; i<result.size() ; i++)
4775         {
4776         trace("RES:%s", result[i].c_str());
4777         }
4778     */
4780     
4781     return true;
4784 /**
4785  * Parse a <filelist> entry.  This is far simpler than FileSet,
4786  * since no directory scanning is needed.  The file names are listed
4787  * explicitly.
4788  */  
4789 bool MakeBase::parseFileList(Element *elem,
4790                           MakeBase &propRef,
4791                           FileList &fileList)
4793     std::vector<String> fnames;
4794     //Look for child tags, namely "file"
4795     std::vector<Element *> children  = elem->getChildren();
4796     for (unsigned int i=0 ; i<children.size() ; i++)
4797         {
4798         Element *child = children[i];
4799         String tagName = child->getName();
4800         if (tagName == "file")
4801             {
4802             String fname = child->getAttribute("name");
4803             if (fname.size()==0)
4804                 {
4805                 error("<file> element requires name="" attribute");
4806                 return false;
4807                 }
4808             fnames.push_back(fname);
4809             }
4810         else
4811             {
4812             error("tag <%s> not allowed in <fileset>", tagName.c_str());
4813             return false;
4814             }
4815         }
4817     String dir;
4818     //Get the base directory for reading file names
4819     if (!propRef.getAttribute(elem, "dir", dir))
4820         return false;
4821     fileList.setDirectory(dir);
4822     fileList.setFiles(fnames);
4824     return true;
4829 /**
4830  * Create a directory, making intermediate dirs
4831  * if necessary
4832  */                  
4833 bool MakeBase::createDirectory(const String &dirname)
4835     //trace("## createDirectory: %s", dirname.c_str());
4836     //## first check if it exists
4837     struct stat finfo;
4838     String nativeDir = getNativePath(dirname);
4839     char *cnative = (char *) nativeDir.c_str();
4840 #ifdef __WIN32__
4841     if (strlen(cnative)==2 && cnative[1]==':')
4842         return true;
4843 #endif
4844     if (cachedStat(nativeDir, &finfo)==0)
4845         {
4846         if (!S_ISDIR(finfo.st_mode))
4847             {
4848             error("mkdir: file %s exists but is not a directory",
4849                   cnative);
4850             return false;
4851             }
4852         else //exists
4853             {
4854             return true;
4855             }
4856         }
4858     //## 2: pull off the last path segment, if any,
4859     //## to make the dir 'above' this one, if necessary
4860     unsigned int pos = dirname.find_last_of('/');
4861     if (pos>0 && pos != dirname.npos)
4862         {
4863         String subpath = dirname.substr(0, pos);
4864         //A letter root (c:) ?
4865         if (!createDirectory(subpath))
4866             return false;
4867         }
4868         
4869     //## 3: now make
4870 #ifdef __WIN32__
4871     if (mkdir(cnative)<0)
4872 #else
4873     if (mkdir(cnative, S_IRWXU | S_IRWXG | S_IRWXO)<0)
4874 #endif
4875         {
4876         error("cannot make directory '%s' : %s",
4877                  cnative, strerror(errno));
4878         return false;
4879         }
4881     removeFromStatCache(nativeDir);
4882         
4883     return true;
4887 /**
4888  * Remove a directory recursively
4889  */ 
4890 bool MakeBase::removeDirectory(const String &dirName)
4892     char *dname = (char *)dirName.c_str();
4894     DIR *dir = opendir(dname);
4895     if (!dir)
4896         {
4897         //# Let this fail nicely.
4898         return true;
4899         //error("error opening directory %s : %s", dname, strerror(errno));
4900         //return false;
4901         }
4902     
4903     while (true)
4904         {
4905         struct dirent *de = readdir(dir);
4906         if (!de)
4907             break;
4909         //Get the directory member name
4910         String s = de->d_name;
4911         if (s.size() == 0 || s[0] == '.')
4912             continue;
4913         String childName;
4914         if (dirName.size() > 0)
4915             {
4916             childName.append(dirName);
4917             childName.append("/");
4918             }
4919         childName.append(s);
4922         struct stat finfo;
4923         String childNative = getNativePath(childName);
4924         char *cnative = (char *)childNative.c_str();
4925         if (cachedStat(childNative, &finfo)<0)
4926             {
4927             error("cannot stat file:%s", cnative);
4928             }
4929         else if (S_ISDIR(finfo.st_mode))
4930             {
4931             //trace("DEL dir: %s", childName.c_str());
4932             if (!removeDirectory(childName))
4933                 {
4934                 return false;
4935                 }
4936             }
4937         else if (!S_ISREG(finfo.st_mode))
4938             {
4939             //trace("not regular: %s", cnative);
4940             }
4941         else
4942             {
4943             //trace("DEL file: %s", childName.c_str());
4944             if (!removeFile(childName))
4945                 {
4946                 return false;
4947                 }
4948             }
4949         }
4950     closedir(dir);
4952     //Now delete the directory
4953     String native = getNativePath(dirName);
4954     if (rmdir(native.c_str())<0)
4955         {
4956         error("could not delete directory %s : %s",
4957             native.c_str() , strerror(errno));
4958         return false;
4959         }
4961     removeFromStatCache(native);
4963     return true;
4964     
4968 /**
4969  * Copy a file from one name to another. Perform only if needed
4970  */ 
4971 bool MakeBase::copyFile(const String &srcFile, const String &destFile)
4973     //# 1 Check up-to-date times
4974     String srcNative = getNativePath(srcFile);
4975     struct stat srcinfo;
4976     if (cachedStat(srcNative, &srcinfo)<0)
4977         {
4978         error("source file %s for copy does not exist",
4979                  srcNative.c_str());
4980         return false;
4981         }
4983     String destNative = getNativePath(destFile);
4984     struct stat destinfo;
4985     if (cachedStat(destNative, &destinfo)==0)
4986         {
4987         if (destinfo.st_mtime >= srcinfo.st_mtime)
4988             return true;
4989         }
4990         
4991     //# 2 prepare a destination directory if necessary
4992     unsigned int pos = destFile.find_last_of('/');
4993     if (pos != destFile.npos)
4994         {
4995         String subpath = destFile.substr(0, pos);
4996         if (!createDirectory(subpath))
4997             return false;
4998         }
5000     //# 3 do the data copy
5001 #ifndef __WIN32__
5003     FILE *srcf = fopen(srcNative.c_str(), "rb");
5004     if (!srcf)
5005         {
5006         error("copyFile cannot open '%s' for reading", srcNative.c_str());
5007         return false;
5008         }
5009     FILE *destf = fopen(destNative.c_str(), "wb");
5010     if (!destf)
5011         {
5012         error("copyFile cannot open %s for writing", srcNative.c_str());
5013         return false;
5014         }
5016     while (!feof(srcf))
5017         {
5018         int ch = fgetc(srcf);
5019         if (ch<0)
5020             break;
5021         fputc(ch, destf);
5022         }
5024     fclose(destf);
5025     fclose(srcf);
5027 #else
5028     
5029     if (!CopyFile(srcNative.c_str(), destNative.c_str(), false))
5030         {
5031         error("copyFile from %s to %s failed",
5032              srcNative.c_str(), destNative.c_str());
5033         return false;
5034         }
5035         
5036 #endif /* __WIN32__ */
5038     removeFromStatCache(destNative);
5040     return true;
5044 /**
5045  * Delete a file
5046  */ 
5047 bool MakeBase::removeFile(const String &file)
5049     String native = getNativePath(file);
5051     if (!fileExists(native))
5052         {
5053         return true;
5054         }
5056 #ifdef WIN32
5057     // On Windows 'remove' will only delete files
5059     if (remove(native.c_str())<0)
5060         {
5061         if (errno==EACCES)
5062             {
5063             error("File %s is read-only", native.c_str());
5064             }
5065         else if (errno==ENOENT)
5066             {
5067             error("File %s does not exist or is a directory", native.c_str());
5068             }
5069         else
5070             {
5071             error("Failed to delete file %s: %s", native.c_str(), strerror(errno));
5072             }
5073         return false;
5074         }
5076 #else
5078     if (!isRegularFile(native))
5079         {
5080         error("File %s does not exist or is not a regular file", native.c_str());
5081         return false;
5082         }
5084     if (remove(native.c_str())<0)
5085         {
5086         if (errno==EACCES)
5087             {
5088             error("File %s is read-only", native.c_str());
5089             }
5090         else
5091             {
5092             error(
5093                 errno==EACCES ? "File %s is read-only" :
5094                 errno==ENOENT ? "File %s does not exist or is a directory" :
5095                 "Failed to delete file %s: %s", native.c_str());
5096             }
5097         return false;
5098         }
5100 #endif
5102     removeFromStatCache(native);
5106 /**
5107  * Tests if the file exists
5108  */ 
5109 bool MakeBase::fileExists(const String &fileName)
5111     String native = getNativePath(fileName);
5112     struct stat finfo;
5113     
5114     //Exists?
5115     if (cachedStat(native, &finfo)<0)
5116         return false;
5118     return true;
5122 /**
5123  * Tests if the file exists and is a regular file
5124  */ 
5125 bool MakeBase::isRegularFile(const String &fileName)
5127     String native = getNativePath(fileName);
5128     struct stat finfo;
5129     
5130     //Exists?
5131     if (cachedStat(native, &finfo)<0)
5132         return false;
5135     //check the file mode
5136     if (!S_ISREG(finfo.st_mode))
5137         return false;
5139     return true;
5142 /**
5143  * Tests if the file exists and is a directory
5144  */ 
5145 bool MakeBase::isDirectory(const String &fileName)
5147     String native = getNativePath(fileName);
5148     struct stat finfo;
5149     
5150     //Exists?
5151     if (cachedStat(native, &finfo)<0)
5152         return false;
5155     //check the file mode
5156     if (!S_ISDIR(finfo.st_mode))
5157         return false;
5159     return true;
5164 /**
5165  * Tests is the modification of fileA is newer than fileB
5166  */ 
5167 bool MakeBase::isNewerThan(const String &fileA, const String &fileB)
5169     //trace("isNewerThan:'%s' , '%s'", fileA.c_str(), fileB.c_str());
5170     String nativeA = getNativePath(fileA);
5171     struct stat infoA;
5172     //IF source does not exist, NOT newer
5173     if (cachedStat(nativeA, &infoA)<0)
5174         {
5175         return false;
5176         }
5178     String nativeB = getNativePath(fileB);
5179     struct stat infoB;
5180     //IF dest does not exist, YES, newer
5181     if (cachedStat(nativeB, &infoB)<0)
5182         {
5183         return true;
5184         }
5186     //check the actual times
5187     if (infoA.st_mtime > infoB.st_mtime)
5188         {
5189         return true;
5190         }
5192     return false;
5196 //########################################################################
5197 //# P K G    C O N F I G
5198 //########################################################################
5201 /**
5202  * Get a character from the buffer at pos.  If out of range,
5203  * return -1 for safety
5204  */
5205 int PkgConfig::get(int pos)
5207     if (pos>parselen)
5208         return -1;
5209     return parsebuf[pos];
5214 /**
5215  *  Skip over all whitespace characters beginning at pos.  Return
5216  *  the position of the first non-whitespace character.
5217  *  Pkg-config is line-oriented, so check for newline
5218  */
5219 int PkgConfig::skipwhite(int pos)
5221     while (pos < parselen)
5222         {
5223         int ch = get(pos);
5224         if (ch < 0)
5225             break;
5226         if (!isspace(ch))
5227             break;
5228         pos++;
5229         }
5230     return pos;
5234 /**
5235  *  Parse the buffer beginning at pos, for a word.  Fill
5236  *  'ret' with the result.  Return the position after the
5237  *  word.
5238  */
5239 int PkgConfig::getword(int pos, String &ret)
5241     while (pos < parselen)
5242         {
5243         int ch = get(pos);
5244         if (ch < 0)
5245             break;
5246         if (!isalnum(ch) && ch != '_' && ch != '-' && ch != '+' && ch != '.')
5247             break;
5248         ret.push_back((char)ch);
5249         pos++;
5250         }
5251     return pos;
5254 bool PkgConfig::parseRequires()
5256     if (requires.size() == 0)
5257         return true;
5258     parsebuf = (char *)requires.c_str();
5259     parselen = requires.size();
5260     int pos = 0;
5261     while (pos < parselen)
5262         {
5263         pos = skipwhite(pos);
5264         String val;
5265         int pos2 = getword(pos, val);
5266         if (pos2 == pos)
5267             break;
5268         pos = pos2;
5269         //trace("val %s", val.c_str());
5270         requireList.push_back(val);
5271         }
5272     return true;
5276 static int getint(const String str)
5278     char *s = (char *)str.c_str();
5279     char *ends = NULL;
5280     long val = strtol(s, &ends, 10);
5281     if (ends == s)
5282         return 0L;
5283     else
5284         return val;
5287 void PkgConfig::parseVersion()
5289     if (version.size() == 0)
5290         return;
5291     String s1, s2, s3;
5292     unsigned int pos = 0;
5293     unsigned int pos2 = version.find('.', pos);
5294     if (pos2 == version.npos)
5295         {
5296         s1 = version;
5297         }
5298     else
5299         {
5300         s1 = version.substr(pos, pos2-pos);
5301         pos = pos2;
5302         pos++;
5303         if (pos < version.size())
5304             {
5305             pos2 = version.find('.', pos);
5306             if (pos2 == version.npos)
5307                 {
5308                 s2 = version.substr(pos, version.size()-pos);
5309                 }
5310             else
5311                 {
5312                 s2 = version.substr(pos, pos2-pos);
5313                 pos = pos2;
5314                 pos++;
5315                 if (pos < version.size())
5316                     s3 = version.substr(pos, pos2-pos);
5317                 }
5318             }
5319         }
5321     majorVersion = getint(s1);
5322     minorVersion = getint(s2);
5323     microVersion = getint(s3);
5324     //trace("version:%d.%d.%d", majorVersion,
5325     //          minorVersion, microVersion );
5329 bool PkgConfig::parseLine(const String &lineBuf)
5331     parsebuf = (char *)lineBuf.c_str();
5332     parselen = lineBuf.size();
5333     int pos = 0;
5334     
5335     while (pos < parselen)
5336         {
5337         String attrName;
5338         pos = skipwhite(pos);
5339         int ch = get(pos);
5340         if (ch == '#')
5341             {
5342             //comment.  eat the rest of the line
5343             while (pos < parselen)
5344                 {
5345                 ch = get(pos);
5346                 if (ch == '\n' || ch < 0)
5347                     break;
5348                 pos++;
5349                 }
5350             continue;
5351             }
5352         pos = getword(pos, attrName);
5353         if (attrName.size() == 0)
5354             continue;
5355         
5356         pos = skipwhite(pos);
5357         ch = get(pos);
5358         if (ch != ':' && ch != '=')
5359             {
5360             error("expected ':' or '='");
5361             return false;
5362             }
5363         pos++;
5364         pos = skipwhite(pos);
5365         String attrVal;
5366         while (pos < parselen)
5367             {
5368             ch = get(pos);
5369             if (ch == '\n' || ch < 0)
5370                 break;
5371             else if (ch == '$' && get(pos+1) == '{')
5372                 {
5373                 //#  this is a ${substitution}
5374                 pos += 2;
5375                 String subName;
5376                 while (pos < parselen)
5377                     {
5378                     ch = get(pos);
5379                     if (ch < 0)
5380                         {
5381                         error("unterminated substitution");
5382                         return false;
5383                         }
5384                     else if (ch == '}')
5385                         break;
5386                     else
5387                         subName.push_back((char)ch);
5388                     pos++;
5389                     }
5390                 //trace("subName:%s %s", subName.c_str(), prefix.c_str());
5391                 if (subName == "prefix" && prefix.size()>0)
5392                     {
5393                     attrVal.append(prefix);
5394                     //trace("prefix override:%s", prefix.c_str());
5395                     }
5396                 else
5397                     {
5398                     String subVal = attrs[subName];
5399                     //trace("subVal:%s", subVal.c_str());
5400                     attrVal.append(subVal);
5401                     }
5402                 }
5403             else
5404                 attrVal.push_back((char)ch);
5405             pos++;
5406             }
5408         attrVal = trim(attrVal);
5409         attrs[attrName] = attrVal;
5411         String attrNameL = toLower(attrName);
5413         if (attrNameL == "name")
5414             name = attrVal;
5415         else if (attrNameL == "description")
5416             description = attrVal;
5417         else if (attrNameL == "cflags")
5418             cflags = attrVal;
5419         else if (attrNameL == "libs")
5420             libs = attrVal;
5421         else if (attrNameL == "requires")
5422             requires = attrVal;
5423         else if (attrNameL == "version")
5424             version = attrVal;
5426         //trace("name:'%s'  value:'%s'",
5427         //      attrName.c_str(), attrVal.c_str());
5428         }
5430     return true;
5434 bool PkgConfig::parse(const String &buf)
5436     init();
5438     String line;
5439     int lineNr = 0;
5440     for (unsigned int p=0 ; p<buf.size() ; p++)
5441         {
5442         int ch = buf[p];
5443         if (ch == '\n' || ch == '\r')
5444             {
5445             if (!parseLine(line))
5446                 return false;
5447             line.clear();
5448             lineNr++;
5449             }
5450         else
5451             {
5452             line.push_back(ch);
5453             }
5454         }
5455     if (line.size()>0)
5456         {
5457         if (!parseLine(line))
5458             return false;
5459         }
5461     parseRequires();
5462     parseVersion();
5464     return true;
5470 void PkgConfig::dumpAttrs()
5472     //trace("### PkgConfig attributes for %s", fileName.c_str());
5473     std::map<String, String>::iterator iter;
5474     for (iter=attrs.begin() ; iter!=attrs.end() ; iter++)
5475         {
5476         trace("   %s = %s", iter->first.c_str(), iter->second.c_str());
5477         }
5481 bool PkgConfig::readFile(const String &fname)
5483     fileName = getNativePath(fname);
5485     FILE *f = fopen(fileName.c_str(), "r");
5486     if (!f)
5487         {
5488         error("cannot open file '%s' for reading", fileName.c_str());
5489         return false;
5490         }
5491     String buf;
5492     while (true)
5493         {
5494         int ch = fgetc(f);
5495         if (ch < 0)
5496             break;
5497         buf.push_back((char)ch);
5498         }
5499     fclose(f);
5501     //trace("####### File:\n%s", buf.c_str());
5502     if (!parse(buf))
5503         {
5504         return false;
5505         }
5507     //dumpAttrs();
5509     return true;
5514 bool PkgConfig::query(const String &pkgName)
5516     name = pkgName;
5518     String fname = path;
5519     fname.append("/");
5520     fname.append(name);
5521     fname.append(".pc");
5523     if (!readFile(fname))
5524         {
5525         error("Cannot find package '%s'. Do you have it installed?",
5526                        pkgName.c_str());
5527         return false;
5528         }
5529     
5530     return true;
5537 //########################################################################
5538 //# D E P T O O L
5539 //########################################################################
5543 /**
5544  *  Class which holds information for each file.
5545  */
5546 class FileRec
5548 public:
5550     typedef enum
5551         {
5552         UNKNOWN,
5553         CFILE,
5554         HFILE,
5555         OFILE
5556         } FileType;
5558     /**
5559      *  Constructor
5560      */
5561     FileRec()
5562         { init(); type = UNKNOWN; }
5564     /**
5565      *  Copy constructor
5566      */
5567     FileRec(const FileRec &other)
5568         { init(); assign(other); }
5569     /**
5570      *  Constructor
5571      */
5572     FileRec(int typeVal)
5573         { init(); type = typeVal; }
5574     /**
5575      *  Assignment operator
5576      */
5577     FileRec &operator=(const FileRec &other)
5578         { init(); assign(other); return *this; }
5581     /**
5582      *  Destructor
5583      */
5584     ~FileRec()
5585         {}
5587     /**
5588      *  Directory part of the file name
5589      */
5590     String path;
5592     /**
5593      *  Base name, sans directory and suffix
5594      */
5595     String baseName;
5597     /**
5598      *  File extension, such as cpp or h
5599      */
5600     String suffix;
5602     /**
5603      *  Type of file: CFILE, HFILE, OFILE
5604      */
5605     int type;
5607     /**
5608      * Used to list files ref'd by this one
5609      */
5610     std::map<String, FileRec *> files;
5613 private:
5615     void init()
5616         {
5617         }
5619     void assign(const FileRec &other)
5620         {
5621         type     = other.type;
5622         baseName = other.baseName;
5623         suffix   = other.suffix;
5624         files    = other.files;
5625         }
5627 };
5631 /**
5632  *  Simpler dependency record
5633  */
5634 class DepRec
5636 public:
5638     /**
5639      *  Constructor
5640      */
5641     DepRec()
5642         {init();}
5644     /**
5645      *  Copy constructor
5646      */
5647     DepRec(const DepRec &other)
5648         {init(); assign(other);}
5649     /**
5650      *  Constructor
5651      */
5652     DepRec(const String &fname)
5653         {init(); name = fname; }
5654     /**
5655      *  Assignment operator
5656      */
5657     DepRec &operator=(const DepRec &other)
5658         {init(); assign(other); return *this;}
5661     /**
5662      *  Destructor
5663      */
5664     ~DepRec()
5665         {}
5667     /**
5668      *  Directory part of the file name
5669      */
5670     String path;
5672     /**
5673      *  Base name, without the path and suffix
5674      */
5675     String name;
5677     /**
5678      *  Suffix of the source
5679      */
5680     String suffix;
5683     /**
5684      * Used to list files ref'd by this one
5685      */
5686     std::vector<String> files;
5689 private:
5691     void init()
5692         {
5693         }
5695     void assign(const DepRec &other)
5696         {
5697         path     = other.path;
5698         name     = other.name;
5699         suffix   = other.suffix;
5700         files    = other.files; //avoid recursion
5701         }
5703 };
5706 class DepTool : public MakeBase
5708 public:
5710     /**
5711      *  Constructor
5712      */
5713     DepTool()
5714         { init(); }
5716     /**
5717      *  Copy constructor
5718      */
5719     DepTool(const DepTool &other)
5720         { init(); assign(other); }
5722     /**
5723      *  Assignment operator
5724      */
5725     DepTool &operator=(const DepTool &other)
5726         { init(); assign(other); return *this; }
5729     /**
5730      *  Destructor
5731      */
5732     ~DepTool()
5733         {}
5736     /**
5737      *  Reset this section of code
5738      */
5739     virtual void init();
5740     
5741     /**
5742      *  Reset this section of code
5743      */
5744     virtual void assign(const DepTool &other)
5745         {
5746         }
5747     
5748     /**
5749      *  Sets the source directory which will be scanned
5750      */
5751     virtual void setSourceDirectory(const String &val)
5752         { sourceDir = val; }
5754     /**
5755      *  Returns the source directory which will be scanned
5756      */
5757     virtual String getSourceDirectory()
5758         { return sourceDir; }
5760     /**
5761      *  Sets the list of files within the directory to analyze
5762      */
5763     virtual void setFileList(const std::vector<String> &list)
5764         { fileList = list; }
5766     /**
5767      * Creates the list of all file names which will be
5768      * candidates for further processing.  Reads make.exclude
5769      * to see which files for directories to leave out.
5770      */
5771     virtual bool createFileList();
5774     /**
5775      *  Generates the forward dependency list
5776      */
5777     virtual bool generateDependencies();
5780     /**
5781      *  Generates the forward dependency list, saving the file
5782      */
5783     virtual bool generateDependencies(const String &);
5786     /**
5787      *  Load a dependency file
5788      */
5789     std::vector<DepRec> loadDepFile(const String &fileName);
5791     /**
5792      *  Load a dependency file, generating one if necessary
5793      */
5794     std::vector<DepRec> getDepFile(const String &fileName,
5795               bool forceRefresh);
5797     /**
5798      *  Save a dependency file
5799      */
5800     bool saveDepFile(const String &fileName);
5803 private:
5806     /**
5807      *
5808      */
5809     void parseName(const String &fullname,
5810                    String &path,
5811                    String &basename,
5812                    String &suffix);
5814     /**
5815      *
5816      */
5817     int get(int pos);
5819     /**
5820      *
5821      */
5822     int skipwhite(int pos);
5824     /**
5825      *
5826      */
5827     int getword(int pos, String &ret);
5829     /**
5830      *
5831      */
5832     bool sequ(int pos, const char *key);
5834     /**
5835      *
5836      */
5837     bool addIncludeFile(FileRec *frec, const String &fname);
5839     /**
5840      *
5841      */
5842     bool scanFile(const String &fname, FileRec *frec);
5844     /**
5845      *
5846      */
5847     bool processDependency(FileRec *ofile, FileRec *include);
5849     /**
5850      *
5851      */
5852     String sourceDir;
5854     /**
5855      *
5856      */
5857     std::vector<String> fileList;
5859     /**
5860      *
5861      */
5862     std::vector<String> directories;
5864     /**
5865      * A list of all files which will be processed for
5866      * dependencies.
5867      */
5868     std::map<String, FileRec *> allFiles;
5870     /**
5871      * The list of .o files, and the
5872      * dependencies upon them.
5873      */
5874     std::map<String, FileRec *> oFiles;
5876     int depFileSize;
5877     char *depFileBuf;
5879     static const int readBufSize = 8192;
5880     char readBuf[8193];//byte larger
5882 };
5888 /**
5889  *  Clean up after processing.  Called by the destructor, but should
5890  *  also be called before the object is reused.
5891  */
5892 void DepTool::init()
5894     sourceDir = ".";
5896     fileList.clear();
5897     directories.clear();
5898     
5899     //clear output file list
5900     std::map<String, FileRec *>::iterator iter;
5901     for (iter=oFiles.begin(); iter!=oFiles.end() ; iter++)
5902         delete iter->second;
5903     oFiles.clear();
5905     //allFiles actually contains the master copies. delete them
5906     for (iter= allFiles.begin(); iter!=allFiles.end() ; iter++)
5907         delete iter->second;
5908     allFiles.clear(); 
5915 /**
5916  *  Parse a full path name into path, base name, and suffix
5917  */
5918 void DepTool::parseName(const String &fullname,
5919                         String &path,
5920                         String &basename,
5921                         String &suffix)
5923     if (fullname.size() < 2)
5924         return;
5926     unsigned int pos = fullname.find_last_of('/');
5927     if (pos != fullname.npos && pos<fullname.size()-1)
5928         {
5929         path = fullname.substr(0, pos);
5930         pos++;
5931         basename = fullname.substr(pos, fullname.size()-pos);
5932         }
5933     else
5934         {
5935         path = "";
5936         basename = fullname;
5937         }
5939     pos = basename.find_last_of('.');
5940     if (pos != basename.npos && pos<basename.size()-1)
5941         {
5942         suffix   = basename.substr(pos+1, basename.size()-pos-1);
5943         basename = basename.substr(0, pos);
5944         }
5946     //trace("parsename:%s %s %s", path.c_str(),
5947     //        basename.c_str(), suffix.c_str()); 
5952 /**
5953  *  Generate our internal file list.
5954  */
5955 bool DepTool::createFileList()
5958     for (unsigned int i=0 ; i<fileList.size() ; i++)
5959         {
5960         String fileName = fileList[i];
5961         //trace("## FileName:%s", fileName.c_str());
5962         String path;
5963         String basename;
5964         String sfx;
5965         parseName(fileName, path, basename, sfx);
5966         if (sfx == "cpp" || sfx == "c" || sfx == "cxx"   ||
5967             sfx == "cc" || sfx == "CC")
5968             {
5969             FileRec *fe         = new FileRec(FileRec::CFILE);
5970             fe->path            = path;
5971             fe->baseName        = basename;
5972             fe->suffix          = sfx;
5973             allFiles[fileName]  = fe;
5974             }
5975         else if (sfx == "h"   ||  sfx == "hh"  ||
5976                  sfx == "hpp" ||  sfx == "hxx")
5977             {
5978             FileRec *fe         = new FileRec(FileRec::HFILE);
5979             fe->path            = path;
5980             fe->baseName        = basename;
5981             fe->suffix          = sfx;
5982             allFiles[fileName]  = fe;
5983             }
5984         }
5986     if (!listDirectories(sourceDir, "", directories))
5987         return false;
5988         
5989     return true;
5996 /**
5997  * Get a character from the buffer at pos.  If out of range,
5998  * return -1 for safety
5999  */
6000 int DepTool::get(int pos)
6002     if (pos>depFileSize)
6003         return -1;
6004     return depFileBuf[pos];
6009 /**
6010  *  Skip over all whitespace characters beginning at pos.  Return
6011  *  the position of the first non-whitespace character.
6012  */
6013 int DepTool::skipwhite(int pos)
6015     while (pos < depFileSize)
6016         {
6017         int ch = get(pos);
6018         if (ch < 0)
6019             break;
6020         if (!isspace(ch))
6021             break;
6022         pos++;
6023         }
6024     return pos;
6028 /**
6029  *  Parse the buffer beginning at pos, for a word.  Fill
6030  *  'ret' with the result.  Return the position after the
6031  *  word.
6032  */
6033 int DepTool::getword(int pos, String &ret)
6035     while (pos < depFileSize)
6036         {
6037         int ch = get(pos);
6038         if (ch < 0)
6039             break;
6040         if (isspace(ch))
6041             break;
6042         ret.push_back((char)ch);
6043         pos++;
6044         }
6045     return pos;
6048 /**
6049  * Return whether the sequence of characters in the buffer
6050  * beginning at pos match the key,  for the length of the key
6051  */
6052 bool DepTool::sequ(int pos, const char *key)
6054     while (*key)
6055         {
6056         if (*key != get(pos))
6057             return false;
6058         key++; pos++;
6059         }
6060     return true;
6065 /**
6066  *  Add an include file name to a file record.  If the name
6067  *  is not found in allFiles explicitly, try prepending include
6068  *  directory names to it and try again.
6069  */
6070 bool DepTool::addIncludeFile(FileRec *frec, const String &iname)
6072     //# if the name is an exact match to a path name
6073     //# in allFiles, like "myinc.h"
6074     std::map<String, FileRec *>::iterator iter =
6075            allFiles.find(iname);
6076     if (iter != allFiles.end()) //already exists
6077         {
6078          //h file in same dir
6079         FileRec *other = iter->second;
6080         //trace("local: '%s'", iname.c_str());
6081         frec->files[iname] = other;
6082         return true;
6083         }
6084     else 
6085         {
6086         //## Ok, it was not found directly
6087         //look in other dirs
6088         std::vector<String>::iterator diter;
6089         for (diter=directories.begin() ;
6090              diter!=directories.end() ; diter++)
6091             {
6092             String dfname = *diter;
6093             dfname.append("/");
6094             dfname.append(iname);
6095             URI fullPathURI(dfname);  //normalize path name
6096             String fullPath = fullPathURI.getPath();
6097             if (fullPath[0] == '/')
6098                 fullPath = fullPath.substr(1);
6099             //trace("Normalized %s to %s", dfname.c_str(), fullPath.c_str());
6100             iter = allFiles.find(fullPath);
6101             if (iter != allFiles.end())
6102                 {
6103                 FileRec *other = iter->second;
6104                 //trace("other: '%s'", iname.c_str());
6105                 frec->files[fullPath] = other;
6106                 return true;
6107                 }
6108             }
6109         }
6110     return true;
6115 /**
6116  *  Lightly parse a file to find the #include directives.  Do
6117  *  a bit of state machine stuff to make sure that the directive
6118  *  is valid.  (Like not in a comment).
6119  */
6120 bool DepTool::scanFile(const String &fname, FileRec *frec)
6122     String fileName;
6123     if (sourceDir.size() > 0)
6124         {
6125         fileName.append(sourceDir);
6126         fileName.append("/");
6127         }
6128     fileName.append(fname);
6129     String nativeName = getNativePath(fileName);
6130     FILE *f = fopen(nativeName.c_str(), "r");
6131     if (!f)
6132         {
6133         error("Could not open '%s' for reading", fname.c_str());
6134         return false;
6135         }
6136     String buf;
6137     while (!feof(f))
6138         {
6139         int nrbytes = fread(readBuf, 1, readBufSize, f);
6140         readBuf[nrbytes] = '\0';
6141         buf.append(readBuf);
6142         }
6143     fclose(f);
6145     depFileSize = buf.size();
6146     depFileBuf  = (char *)buf.c_str();
6147     int pos = 0;
6150     while (pos < depFileSize)
6151         {
6152         //trace("p:%c", get(pos));
6154         //# Block comment
6155         if (get(pos) == '/' && get(pos+1) == '*')
6156             {
6157             pos += 2;
6158             while (pos < depFileSize)
6159                 {
6160                 if (get(pos) == '*' && get(pos+1) == '/')
6161                     {
6162                     pos += 2;
6163                     break;
6164                     }
6165                 else
6166                     pos++;
6167                 }
6168             }
6169         //# Line comment
6170         else if (get(pos) == '/' && get(pos+1) == '/')
6171             {
6172             pos += 2;
6173             while (pos < depFileSize)
6174                 {
6175                 if (get(pos) == '\n')
6176                     {
6177                     pos++;
6178                     break;
6179                     }
6180                 else
6181                     pos++;
6182                 }
6183             }
6184         //# #include! yaay
6185         else if (sequ(pos, "#include"))
6186             {
6187             pos += 8;
6188             pos = skipwhite(pos);
6189             String iname;
6190             pos = getword(pos, iname);
6191             if (iname.size()>2)
6192                 {
6193                 iname = iname.substr(1, iname.size()-2);
6194                 addIncludeFile(frec, iname);
6195                 }
6196             }
6197         else
6198             {
6199             pos++;
6200             }
6201         }
6203     return true;
6208 /**
6209  *  Recursively check include lists to find all files in allFiles to which
6210  *  a given file is dependent.
6211  */
6212 bool DepTool::processDependency(FileRec *ofile, FileRec *include)
6214     std::map<String, FileRec *>::iterator iter;
6215     for (iter=include->files.begin() ; iter!=include->files.end() ; iter++)
6216         {
6217         String fname  = iter->first;
6218         if (ofile->files.find(fname) != ofile->files.end())
6219             {
6220             //trace("file '%s' already seen", fname.c_str());
6221             continue;
6222             }
6223         FileRec *child  = iter->second;
6224         ofile->files[fname] = child;
6225       
6226         processDependency(ofile, child);
6227         }
6230     return true;
6237 /**
6238  *  Generate the file dependency list.
6239  */
6240 bool DepTool::generateDependencies()
6242     std::map<String, FileRec *>::iterator iter;
6243     //# First pass.  Scan for all includes
6244     for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
6245         {
6246         FileRec *frec = iter->second;
6247         if (!scanFile(iter->first, frec))
6248             {
6249             //quit?
6250             }
6251         }
6253     //# Second pass.  Scan for all includes
6254     for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
6255         {
6256         FileRec *include = iter->second;
6257         if (include->type == FileRec::CFILE)
6258             {
6259             //String cFileName   = iter->first;
6260             FileRec *ofile     = new FileRec(FileRec::OFILE);
6261             ofile->path        = include->path;
6262             ofile->baseName    = include->baseName;
6263             ofile->suffix      = include->suffix;
6264             String fname       = include->path;
6265             if (fname.size()>0)
6266                 fname.append("/");
6267             fname.append(include->baseName);
6268             fname.append(".o");
6269             oFiles[fname]    = ofile;
6270             //add the .c file first?   no, don't
6271             //ofile->files[cFileName] = include;
6272             
6273             //trace("ofile:%s", fname.c_str());
6275             processDependency(ofile, include);
6276             }
6277         }
6279       
6280     return true;
6285 /**
6286  *  High-level call to generate deps and optionally save them
6287  */
6288 bool DepTool::generateDependencies(const String &fileName)
6290     if (!createFileList())
6291         return false;
6292     if (!generateDependencies())
6293         return false;
6294     if (!saveDepFile(fileName))
6295         return false;
6296     return true;
6300 /**
6301  *   This saves the dependency cache.
6302  */
6303 bool DepTool::saveDepFile(const String &fileName)
6305     time_t tim;
6306     time(&tim);
6308     FILE *f = fopen(fileName.c_str(), "w");
6309     if (!f)
6310         {
6311         trace("cannot open '%s' for writing", fileName.c_str());
6312         }
6313     fprintf(f, "<?xml version='1.0'?>\n");
6314     fprintf(f, "<!--\n");
6315     fprintf(f, "########################################################\n");
6316     fprintf(f, "## File: build.dep\n");
6317     fprintf(f, "## Generated by BuildTool at :%s", ctime(&tim));
6318     fprintf(f, "########################################################\n");
6319     fprintf(f, "-->\n");
6321     fprintf(f, "<dependencies source='%s'>\n\n", sourceDir.c_str());
6322     std::map<String, FileRec *>::iterator iter;
6323     for (iter=oFiles.begin() ; iter!=oFiles.end() ; iter++)
6324         {
6325         FileRec *frec = iter->second;
6326         if (frec->type == FileRec::OFILE)
6327             {
6328             fprintf(f, "<object path='%s' name='%s' suffix='%s'>\n",
6329                  frec->path.c_str(), frec->baseName.c_str(), frec->suffix.c_str());
6330             std::map<String, FileRec *>::iterator citer;
6331             for (citer=frec->files.begin() ; citer!=frec->files.end() ; citer++)
6332                 {
6333                 String cfname = citer->first;
6334                 fprintf(f, "    <dep name='%s'/>\n", cfname.c_str());
6335                 }
6336             fprintf(f, "</object>\n\n");
6337             }
6338         }
6340     fprintf(f, "</dependencies>\n");
6341     fprintf(f, "\n");
6342     fprintf(f, "<!--\n");
6343     fprintf(f, "########################################################\n");
6344     fprintf(f, "## E N D\n");
6345     fprintf(f, "########################################################\n");
6346     fprintf(f, "-->\n");
6348     fclose(f);
6350     return true;
6356 /**
6357  *   This loads the dependency cache.
6358  */
6359 std::vector<DepRec> DepTool::loadDepFile(const String &depFile)
6361     std::vector<DepRec> result;
6362     
6363     Parser parser;
6364     Element *root = parser.parseFile(depFile.c_str());
6365     if (!root)
6366         {
6367         //error("Could not open %s for reading", depFile.c_str());
6368         return result;
6369         }
6371     if (root->getChildren().size()==0 ||
6372         root->getChildren()[0]->getName()!="dependencies")
6373         {
6374         error("loadDepFile: main xml element should be <dependencies>");
6375         delete root;
6376         return result;
6377         }
6379     //########## Start parsing
6380     Element *depList = root->getChildren()[0];
6382     std::vector<Element *> objects = depList->getChildren();
6383     for (unsigned int i=0 ; i<objects.size() ; i++)
6384         {
6385         Element *objectElem = objects[i];
6386         String tagName = objectElem->getName();
6387         if (tagName != "object")
6388             {
6389             error("loadDepFile: <dependencies> should have only <object> children");
6390             return result;
6391             }
6393         String objName   = objectElem->getAttribute("name");
6394          //trace("object:%s", objName.c_str());
6395         DepRec depObject(objName);
6396         depObject.path   = objectElem->getAttribute("path");
6397         depObject.suffix = objectElem->getAttribute("suffix");
6398         //########## DESCRIPTION
6399         std::vector<Element *> depElems = objectElem->getChildren();
6400         for (unsigned int i=0 ; i<depElems.size() ; i++)
6401             {
6402             Element *depElem = depElems[i];
6403             tagName = depElem->getName();
6404             if (tagName != "dep")
6405                 {
6406                 error("loadDepFile: <object> should have only <dep> children");
6407                 return result;
6408                 }
6409             String depName = depElem->getAttribute("name");
6410             //trace("    dep:%s", depName.c_str());
6411             depObject.files.push_back(depName);
6412             }
6414         //Insert into the result list, in a sorted manner
6415         bool inserted = false;
6416         std::vector<DepRec>::iterator iter;
6417         for (iter = result.begin() ; iter != result.end() ; iter++)
6418             {
6419             String vpath = iter->path;
6420             vpath.append("/");
6421             vpath.append(iter->name);
6422             String opath = depObject.path;
6423             opath.append("/");
6424             opath.append(depObject.name);
6425             if (vpath > opath)
6426                 {
6427                 inserted = true;
6428                 iter = result.insert(iter, depObject);
6429                 break;
6430                 }
6431             }
6432         if (!inserted)
6433             result.push_back(depObject);
6434         }
6436     delete root;
6438     return result;
6442 /**
6443  *   This loads the dependency cache.
6444  */
6445 std::vector<DepRec> DepTool::getDepFile(const String &depFile,
6446                    bool forceRefresh)
6448     std::vector<DepRec> result;
6449     if (forceRefresh)
6450         {
6451         generateDependencies(depFile);
6452         result = loadDepFile(depFile);
6453         }
6454     else
6455         {
6456         //try once
6457         result = loadDepFile(depFile);
6458         if (result.size() == 0)
6459             {
6460             //fail? try again
6461             generateDependencies(depFile);
6462             result = loadDepFile(depFile);
6463             }
6464         }
6465     return result;
6471 //########################################################################
6472 //# T A S K
6473 //########################################################################
6474 //forward decl
6475 class Target;
6476 class Make;
6478 /**
6479  *
6480  */
6481 class Task : public MakeBase
6484 public:
6486     typedef enum
6487         {
6488         TASK_NONE,
6489         TASK_CC,
6490         TASK_COPY,
6491         TASK_CXXTEST_PART,
6492         TASK_CXXTEST_ROOT,
6493         TASK_DELETE,
6494         TASK_ECHO,
6495         TASK_JAR,
6496         TASK_JAVAC,
6497         TASK_LINK,
6498         TASK_MAKEFILE,
6499         TASK_MKDIR,
6500         TASK_MSGFMT,
6501         TASK_PKG_CONFIG,
6502         TASK_RANLIB,
6503         TASK_RC,
6504         TASK_SHAREDLIB,
6505         TASK_STATICLIB,
6506         TASK_STRIP,
6507         TASK_TOUCH,
6508         TASK_TSTAMP
6509         } TaskType;
6510         
6512     /**
6513      *
6514      */
6515     Task(MakeBase &par) : parent(par)
6516         { init(); }
6518     /**
6519      *
6520      */
6521     Task(const Task &other) : parent(other.parent)
6522         { init(); assign(other); }
6524     /**
6525      *
6526      */
6527     Task &operator=(const Task &other)
6528         { assign(other); return *this; }
6530     /**
6531      *
6532      */
6533     virtual ~Task()
6534         { }
6537     /**
6538      *
6539      */
6540     virtual MakeBase &getParent()
6541         { return parent; }
6543      /**
6544      *
6545      */
6546     virtual int  getType()
6547         { return type; }
6549     /**
6550      *
6551      */
6552     virtual void setType(int val)
6553         { type = val; }
6555     /**
6556      *
6557      */
6558     virtual String getName()
6559         { return name; }
6561     /**
6562      *
6563      */
6564     virtual bool execute()
6565         { return true; }
6567     /**
6568      *
6569      */
6570     virtual bool parse(Element *elem)
6571         { return true; }
6573     /**
6574      *
6575      */
6576     Task *createTask(Element *elem, int lineNr);
6579 protected:
6581     void init()
6582         {
6583         type = TASK_NONE;
6584         name = "none";
6585         }
6587     void assign(const Task &other)
6588         {
6589         type = other.type;
6590         name = other.name;
6591         }
6592         
6593     /**
6594      *  Show task status
6595      */
6596     void taskstatus(const char *fmt, ...)
6597         {
6598         va_list args;
6599         va_start(args,fmt);
6600         fprintf(stdout, "    %s : ", name.c_str());
6601         vfprintf(stdout, fmt, args);
6602         fprintf(stdout, "\n");
6603         va_end(args) ;
6604         }
6606     String getAttribute(Element *elem, const String &attrName)
6607         {
6608         String str;
6609         return str;
6610         }
6612     MakeBase &parent;
6614     int type;
6616     String name;
6617 };
6621 /**
6622  * This task runs the C/C++ compiler.  The compiler is invoked
6623  * for all .c or .cpp files which are newer than their correcsponding
6624  * .o files.  
6625  */
6626 class TaskCC : public Task
6628 public:
6630     TaskCC(MakeBase &par) : Task(par)
6631         {
6632         type = TASK_CC;
6633         name = "cc";
6634         }
6636     virtual ~TaskCC()
6637         {}
6638         
6639     virtual bool isExcludedInc(const String &dirname)
6640         {
6641         for (unsigned int i=0 ; i<excludeInc.size() ; i++)
6642             {
6643             String fname = excludeInc[i];
6644             if (fname == dirname)
6645                 return true;
6646             }
6647         return false;
6648         }
6650     virtual bool execute()
6651         {
6652         //evaluate our parameters
6653         String command         = parent.eval(commandOpt, "gcc");
6654         String ccCommand       = parent.eval(ccCommandOpt, "gcc");
6655         String cxxCommand      = parent.eval(cxxCommandOpt, "g++");
6656         String source          = parent.eval(sourceOpt, ".");
6657         String dest            = parent.eval(destOpt, ".");
6658         String flags           = parent.eval(flagsOpt, "");
6659         String defines         = parent.eval(definesOpt, "");
6660         String includes        = parent.eval(includesOpt, "");
6661         bool continueOnError   = parent.evalBool(continueOnErrorOpt, true);
6662         bool refreshCache      = parent.evalBool(refreshCacheOpt, false);
6664         if (!listFiles(parent, fileSet))
6665             return false;
6666             
6667         FILE *f = NULL;
6668         f = fopen("compile.lst", "w");
6670         //refreshCache is probably false here, unless specified otherwise
6671         String fullName = parent.resolve("build.dep");
6672         if (refreshCache || isNewerThan(parent.getURI().getPath(), fullName))
6673             {
6674             taskstatus("regenerating C/C++ dependency cache");
6675             refreshCache = true;
6676             }
6678         DepTool depTool;
6679         depTool.setSourceDirectory(source);
6680         depTool.setFileList(fileSet.getFiles());
6681         std::vector<DepRec> deps =
6682              depTool.getDepFile("build.dep", refreshCache);
6683         
6684         String incs;
6685         incs.append("-I");
6686         incs.append(parent.resolve("."));
6687         incs.append(" ");
6688         if (includes.size()>0)
6689             {
6690             incs.append(includes);
6691             incs.append(" ");
6692             }
6693         std::set<String> paths;
6694         std::vector<DepRec>::iterator viter;
6695         for (viter=deps.begin() ; viter!=deps.end() ; viter++)
6696             {
6697             DepRec dep = *viter;
6698             if (dep.path.size()>0)
6699                 paths.insert(dep.path);
6700             }
6701         if (source.size()>0)
6702             {
6703             incs.append(" -I");
6704             incs.append(parent.resolve(source));
6705             incs.append(" ");
6706             }
6707         std::set<String>::iterator setIter;
6708         for (setIter=paths.begin() ; setIter!=paths.end() ; setIter++)
6709             {
6710             String dirName = *setIter;
6711             //check excludeInc to see if we dont want to include this dir
6712             if (isExcludedInc(dirName))
6713                 continue;
6714             incs.append(" -I");
6715             String dname;
6716             if (source.size()>0)
6717                 {
6718                 dname.append(source);
6719                 dname.append("/");
6720                 }
6721             dname.append(dirName);
6722             incs.append(parent.resolve(dname));
6723             }
6724             
6725         /**
6726          * Compile each of the C files that need it
6727          */
6728         bool errorOccurred = false;                 
6729         std::vector<String> cfiles;
6730         for (viter=deps.begin() ; viter!=deps.end() ; viter++)
6731             {
6732             DepRec dep = *viter;
6734             //## Select command
6735             String sfx = dep.suffix;
6736             String command = ccCommand;
6737             if (sfx == "cpp" || sfx == "cxx" || sfx == "c++" ||
6738                  sfx == "cc" || sfx == "CC")
6739                 command = cxxCommand;
6740  
6741             //## Make paths
6742             String destPath = dest;
6743             String srcPath  = source;
6744             if (dep.path.size()>0)
6745                 {
6746                 destPath.append("/");
6747                 destPath.append(dep.path);
6748                 srcPath.append("/");
6749                 srcPath.append(dep.path);
6750                 }
6751             //## Make sure destination directory exists
6752             if (!createDirectory(destPath))
6753                 return false;
6754                 
6755             //## Check whether it needs to be done
6756             String destName;
6757             if (destPath.size()>0)
6758                 {
6759                 destName.append(destPath);
6760                 destName.append("/");
6761                 }
6762             destName.append(dep.name);
6763             destName.append(".o");
6764             String destFullName = parent.resolve(destName);
6765             String srcName;
6766             if (srcPath.size()>0)
6767                 {
6768                 srcName.append(srcPath);
6769                 srcName.append("/");
6770                 }
6771             srcName.append(dep.name);
6772             srcName.append(".");
6773             srcName.append(dep.suffix);
6774             String srcFullName = parent.resolve(srcName);
6775             bool compileMe = false;
6776             //# First we check if the source is newer than the .o
6777             if (isNewerThan(srcFullName, destFullName))
6778                 {
6779                 taskstatus("compile of %s required by source: %s",
6780                         destFullName.c_str(), srcFullName.c_str());
6781                 compileMe = true;
6782                 }
6783             else
6784                 {
6785                 //# secondly, we check if any of the included dependencies
6786                 //# of the .c/.cpp is newer than the .o
6787                 for (unsigned int i=0 ; i<dep.files.size() ; i++)
6788                     {
6789                     String depName;
6790                     if (source.size()>0)
6791                         {
6792                         depName.append(source);
6793                         depName.append("/");
6794                         }
6795                     depName.append(dep.files[i]);
6796                     String depFullName = parent.resolve(depName);
6797                     bool depRequires = isNewerThan(depFullName, destFullName);
6798                     //trace("%d %s %s\n", depRequires,
6799                     //        destFullName.c_str(), depFullName.c_str());
6800                     if (depRequires)
6801                         {
6802                         taskstatus("compile of %s required by included: %s",
6803                                 destFullName.c_str(), depFullName.c_str());
6804                         compileMe = true;
6805                         break;
6806                         }
6807                     }
6808                 }
6809             if (!compileMe)
6810                 {
6811                 continue;
6812                 }
6814             //## Assemble the command
6815             String cmd = command;
6816             cmd.append(" -c ");
6817             cmd.append(flags);
6818             cmd.append(" ");
6819             cmd.append(defines);
6820             cmd.append(" ");
6821             cmd.append(incs);
6822             cmd.append(" ");
6823             cmd.append(srcFullName);
6824             cmd.append(" -o ");
6825             cmd.append(destFullName);
6827             //## Execute the command
6829             String outString, errString;
6830             bool ret = executeCommand(cmd.c_str(), "", outString, errString);
6832             if (f)
6833                 {
6834                 fprintf(f, "########################### File : %s\n",
6835                              srcFullName.c_str());
6836                 fprintf(f, "#### COMMAND ###\n");
6837                 int col = 0;
6838                 for (unsigned int i = 0 ; i < cmd.size() ; i++)
6839                     {
6840                     char ch = cmd[i];
6841                     if (isspace(ch)  && col > 63)
6842                         {
6843                         fputc('\n', f);
6844                         col = 0;
6845                         }
6846                     else
6847                         {
6848                         fputc(ch, f);
6849                         col++;
6850                         }
6851                     if (col > 76)
6852                         {
6853                         fputc('\n', f);
6854                         col = 0;
6855                         }
6856                     }
6857                 fprintf(f, "\n");
6858                 fprintf(f, "#### STDOUT ###\n%s\n", outString.c_str());
6859                 fprintf(f, "#### STDERR ###\n%s\n\n", errString.c_str());
6860                 fflush(f);
6861                 }
6862             if (!ret)
6863                 {
6864                 error("problem compiling: %s", errString.c_str());
6865                 errorOccurred = true;
6866                 }
6867             if (errorOccurred && !continueOnError)
6868                 break;
6870             removeFromStatCache(getNativePath(destFullName));
6871             }
6873         if (f)
6874             {
6875             fclose(f);
6876             }
6877         
6878         return !errorOccurred;
6879         }
6882     virtual bool parse(Element *elem)
6883         {
6884         String s;
6885         if (!parent.getAttribute(elem, "command", commandOpt))
6886             return false;
6887         if (commandOpt.size()>0)
6888             { cxxCommandOpt = ccCommandOpt = commandOpt; }
6889         if (!parent.getAttribute(elem, "cc", ccCommandOpt))
6890             return false;
6891         if (!parent.getAttribute(elem, "cxx", cxxCommandOpt))
6892             return false;
6893         if (!parent.getAttribute(elem, "destdir", destOpt))
6894             return false;
6895         if (!parent.getAttribute(elem, "continueOnError", continueOnErrorOpt))
6896             return false;
6897         if (!parent.getAttribute(elem, "refreshCache", refreshCacheOpt))
6898             return false;
6900         std::vector<Element *> children = elem->getChildren();
6901         for (unsigned int i=0 ; i<children.size() ; i++)
6902             {
6903             Element *child = children[i];
6904             String tagName = child->getName();
6905             if (tagName == "flags")
6906                 {
6907                 if (!parent.getValue(child, flagsOpt))
6908                     return false;
6909                 flagsOpt = strip(flagsOpt);
6910                 }
6911             else if (tagName == "includes")
6912                 {
6913                 if (!parent.getValue(child, includesOpt))
6914                     return false;
6915                 includesOpt = strip(includesOpt);
6916                 }
6917             else if (tagName == "defines")
6918                 {
6919                 if (!parent.getValue(child, definesOpt))
6920                     return false;
6921                 definesOpt = strip(definesOpt);
6922                 }
6923             else if (tagName == "fileset")
6924                 {
6925                 if (!parseFileSet(child, parent, fileSet))
6926                     return false;
6927                 sourceOpt = fileSet.getDirectory();
6928                 }
6929             else if (tagName == "excludeinc")
6930                 {
6931                 if (!parseFileList(child, parent, excludeInc))
6932                     return false;
6933                 }
6934             }
6936         return true;
6937         }
6938         
6939 protected:
6941     String   commandOpt;
6942     String   ccCommandOpt;
6943     String   cxxCommandOpt;
6944     String   sourceOpt;
6945     String   destOpt;
6946     String   flagsOpt;
6947     String   definesOpt;
6948     String   includesOpt;
6949     String   continueOnErrorOpt;
6950     String   refreshCacheOpt;
6951     FileSet  fileSet;
6952     FileList excludeInc;
6953     
6954 };
6958 /**
6959  *
6960  */
6961 class TaskCopy : public Task
6963 public:
6965     typedef enum
6966         {
6967         CP_NONE,
6968         CP_TOFILE,
6969         CP_TODIR
6970         } CopyType;
6972     TaskCopy(MakeBase &par) : Task(par)
6973         {
6974         type        = TASK_COPY;
6975         name        = "copy";
6976         cptype      = CP_NONE;
6977         haveFileSet = false;
6978         }
6980     virtual ~TaskCopy()
6981         {}
6983     virtual bool execute()
6984         {
6985         String fileName   = parent.eval(fileNameOpt   , ".");
6986         String toFileName = parent.eval(toFileNameOpt , ".");
6987         String toDirName  = parent.eval(toDirNameOpt  , ".");
6988         bool   verbose    = parent.evalBool(verboseOpt, false);
6989         switch (cptype)
6990            {
6991            case CP_TOFILE:
6992                {
6993                if (fileName.size()>0)
6994                    {
6995                    taskstatus("%s to %s",
6996                         fileName.c_str(), toFileName.c_str());
6997                    String fullSource = parent.resolve(fileName);
6998                    String fullDest = parent.resolve(toFileName);
6999                    if (verbose)
7000                        taskstatus("copy %s to file %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            case CP_TODIR:
7019                {
7020                if (haveFileSet)
7021                    {
7022                    if (!listFiles(parent, fileSet))
7023                        return false;
7024                    String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
7026                    taskstatus("%s to %s",
7027                        fileSetDir.c_str(), toDirName.c_str());
7029                    int nrFiles = 0;
7030                    for (unsigned int i=0 ; i<fileSet.size() ; i++)
7031                        {
7032                        String fileName = fileSet[i];
7034                        String sourcePath;
7035                        if (fileSetDir.size()>0)
7036                            {
7037                            sourcePath.append(fileSetDir);
7038                            sourcePath.append("/");
7039                            }
7040                        sourcePath.append(fileName);
7041                        String fullSource = parent.resolve(sourcePath);
7042                        
7043                        //Get the immediate parent directory's base name
7044                        String baseFileSetDir = fileSetDir;
7045                        unsigned int pos = baseFileSetDir.find_last_of('/');
7046                        if (pos!=baseFileSetDir.npos &&
7047                                   pos < baseFileSetDir.size()-1)
7048                            baseFileSetDir =
7049                               baseFileSetDir.substr(pos+1,
7050                                    baseFileSetDir.size());
7051                        //Now make the new path
7052                        String destPath;
7053                        if (toDirName.size()>0)
7054                            {
7055                            destPath.append(toDirName);
7056                            destPath.append("/");
7057                            }
7058                        if (baseFileSetDir.size()>0)
7059                            {
7060                            destPath.append(baseFileSetDir);
7061                            destPath.append("/");
7062                            }
7063                        destPath.append(fileName);
7064                        String fullDest = parent.resolve(destPath);
7065                        //trace("fileName:%s", fileName.c_str());
7066                        if (verbose)
7067                            taskstatus("copy %s to new dir : %s",
7068                                  fullSource.c_str(), fullDest.c_str());
7069                        if (!isNewerThan(fullSource, fullDest))
7070                            {
7071                            if (verbose)
7072                                taskstatus("copy skipping %s", fullSource.c_str());
7073                            continue;
7074                            }
7075                        if (!copyFile(fullSource, fullDest))
7076                            return false;
7077                        nrFiles++;
7078                        }
7079                    taskstatus("%d file(s) copied", nrFiles);
7080                    }
7081                else //file source
7082                    {
7083                    //For file->dir we want only the basename of
7084                    //the source appended to the dest dir
7085                    taskstatus("%s to %s", 
7086                        fileName.c_str(), toDirName.c_str());
7087                    String baseName = fileName;
7088                    unsigned int pos = baseName.find_last_of('/');
7089                    if (pos!=baseName.npos && pos<baseName.size()-1)
7090                        baseName = baseName.substr(pos+1, baseName.size());
7091                    String fullSource = parent.resolve(fileName);
7092                    String destPath;
7093                    if (toDirName.size()>0)
7094                        {
7095                        destPath.append(toDirName);
7096                        destPath.append("/");
7097                        }
7098                    destPath.append(baseName);
7099                    String fullDest = parent.resolve(destPath);
7100                    if (verbose)
7101                        taskstatus("file %s to new dir : %s", fullSource.c_str(),
7102                                           fullDest.c_str());
7103                    if (!isRegularFile(fullSource))
7104                        {
7105                        error("copy : file %s does not exist", fullSource.c_str());
7106                        return false;
7107                        }
7108                    if (!isNewerThan(fullSource, fullDest))
7109                        {
7110                        taskstatus("skipped");
7111                        return true;
7112                        }
7113                    if (!copyFile(fullSource, fullDest))
7114                        return false;
7115                    taskstatus("1 file copied");
7116                    }
7117                return true;
7118                }
7119            }
7120         return true;
7121         }
7124     virtual bool parse(Element *elem)
7125         {
7126         if (!parent.getAttribute(elem, "file", fileNameOpt))
7127             return false;
7128         if (!parent.getAttribute(elem, "tofile", toFileNameOpt))
7129             return false;
7130         if (toFileNameOpt.size() > 0)
7131             cptype = CP_TOFILE;
7132         if (!parent.getAttribute(elem, "todir", toDirNameOpt))
7133             return false;
7134         if (toDirNameOpt.size() > 0)
7135             cptype = CP_TODIR;
7136         if (!parent.getAttribute(elem, "verbose", verboseOpt))
7137             return false;
7138             
7139         haveFileSet = false;
7140         
7141         std::vector<Element *> children = elem->getChildren();
7142         for (unsigned int i=0 ; i<children.size() ; i++)
7143             {
7144             Element *child = children[i];
7145             String tagName = child->getName();
7146             if (tagName == "fileset")
7147                 {
7148                 if (!parseFileSet(child, parent, fileSet))
7149                     {
7150                     error("problem getting fileset");
7151                     return false;
7152                     }
7153                 haveFileSet = true;
7154                 }
7155             }
7157         //Perform validity checks
7158         if (fileNameOpt.size()>0 && fileSet.size()>0)
7159             {
7160             error("<copy> can only have one of : file= and <fileset>");
7161             return false;
7162             }
7163         if (toFileNameOpt.size()>0 && toDirNameOpt.size()>0)
7164             {
7165             error("<copy> can only have one of : tofile= or todir=");
7166             return false;
7167             }
7168         if (haveFileSet && toDirNameOpt.size()==0)
7169             {
7170             error("a <copy> task with a <fileset> must have : todir=");
7171             return false;
7172             }
7173         if (cptype == CP_TOFILE && fileNameOpt.size()==0)
7174             {
7175             error("<copy> tofile= must be associated with : file=");
7176             return false;
7177             }
7178         if (cptype == CP_TODIR && fileNameOpt.size()==0 && !haveFileSet)
7179             {
7180             error("<copy> todir= must be associated with : file= or <fileset>");
7181             return false;
7182             }
7184         return true;
7185         }
7186         
7187 private:
7189     int cptype;
7190     bool haveFileSet;
7192     FileSet fileSet;
7193     String  fileNameOpt;
7194     String  toFileNameOpt;
7195     String  toDirNameOpt;
7196     String  verboseOpt;
7197 };
7200 /**
7201  * Generate CxxTest files
7202  */
7203 class TaskCxxTestPart: public Task
7205 public:
7207     TaskCxxTestPart(MakeBase &par) : Task(par)
7208          {
7209          type    = TASK_CXXTEST_PART;
7210          name    = "cxxtestpart";
7211          }
7213     virtual ~TaskCxxTestPart()
7214         {}
7216     virtual bool execute()
7217         {
7218         if (!listFiles(parent, fileSet))
7219             return false;
7220         String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
7221                 
7222         String fullDest = parent.resolve(parent.eval(destPathOpt, "."));
7223         String cmd = parent.eval(commandOpt, "cxxtestgen.py");
7224         cmd.append(" --part -o ");
7225         cmd.append(fullDest);
7227         unsigned int newFiles = 0;
7228         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7229             {
7230             String fileName = fileSet[i];
7231             if (getSuffix(fileName) != "h")
7232                 continue;
7233             String sourcePath;
7234             if (fileSetDir.size()>0)
7235                 {
7236                 sourcePath.append(fileSetDir);
7237                 sourcePath.append("/");
7238                 }
7239             sourcePath.append(fileName);
7240             String fullSource = parent.resolve(sourcePath);
7242             cmd.append(" ");
7243             cmd.append(fullSource);
7244             if (isNewerThan(fullSource, fullDest)) newFiles++;
7245             }
7246         
7247         if (newFiles>0) {
7248             size_t const lastSlash = fullDest.find_last_of('/');
7249             if (lastSlash != fullDest.npos) {
7250                 String directory(fullDest, 0, lastSlash);
7251                 if (!createDirectory(directory))
7252                     return false;
7253             }
7255             String outString, errString;
7256             if (!executeCommand(cmd.c_str(), "", outString, errString))
7257                 {
7258                 error("<cxxtestpart> problem: %s", errString.c_str());
7259                 return false;
7260                 }
7261             removeFromStatCache(getNativePath(fullDest));
7262         }
7264         return true;
7265         }
7267     virtual bool parse(Element *elem)
7268         {
7269         if (!parent.getAttribute(elem, "command", commandOpt))
7270             return false;
7271         if (!parent.getAttribute(elem, "out", destPathOpt))
7272             return false;
7273             
7274         std::vector<Element *> children = elem->getChildren();
7275         for (unsigned int i=0 ; i<children.size() ; i++)
7276             {
7277             Element *child = children[i];
7278             String tagName = child->getName();
7279             if (tagName == "fileset")
7280                 {
7281                 if (!parseFileSet(child, parent, fileSet))
7282                     return false;
7283                 }
7284             }
7285         return true;
7286         }
7288 private:
7290     String  commandOpt;
7291     String  destPathOpt;
7292     FileSet fileSet;
7294 };
7297 /**
7298  * Generate the CxxTest root file
7299  */
7300 class TaskCxxTestRoot: public Task
7302 public:
7304     TaskCxxTestRoot(MakeBase &par) : Task(par)
7305          {
7306          type    = TASK_CXXTEST_ROOT;
7307          name    = "cxxtestroot";
7308          }
7310     virtual ~TaskCxxTestRoot()
7311         {}
7313     virtual bool execute()
7314         {
7315         if (!listFiles(parent, fileSet))
7316             return false;
7317         String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
7318         unsigned int newFiles = 0;
7319                 
7320         String fullDest = parent.resolve(parent.eval(destPathOpt, "."));
7321         String cmd = parent.eval(commandOpt, "cxxtestgen.py");
7322         cmd.append(" --root -o ");
7323         cmd.append(fullDest);
7324         String templateFile = parent.eval(templateFileOpt, "");
7325         if (templateFile.size()>0) {
7326             String fullTemplate = parent.resolve(templateFile);
7327             cmd.append(" --template=");
7328             cmd.append(fullTemplate);
7329             if (isNewerThan(fullTemplate, fullDest)) newFiles++;
7330         }
7332         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7333             {
7334             String fileName = fileSet[i];
7335             if (getSuffix(fileName) != "h")
7336                 continue;
7337             String sourcePath;
7338             if (fileSetDir.size()>0)
7339                 {
7340                 sourcePath.append(fileSetDir);
7341                 sourcePath.append("/");
7342                 }
7343             sourcePath.append(fileName);
7344             String fullSource = parent.resolve(sourcePath);
7346             cmd.append(" ");
7347             cmd.append(fullSource);
7348             if (isNewerThan(fullSource, fullDest)) newFiles++;
7349             }
7350         
7351         if (newFiles>0) {
7352             size_t const lastSlash = fullDest.find_last_of('/');
7353             if (lastSlash != fullDest.npos) {
7354                 String directory(fullDest, 0, lastSlash);
7355                 if (!createDirectory(directory))
7356                     return false;
7357             }
7359             String outString, errString;
7360             if (!executeCommand(cmd.c_str(), "", outString, errString))
7361                 {
7362                 error("<cxxtestroot> problem: %s", errString.c_str());
7363                 return false;
7364                 }
7365             removeFromStatCache(getNativePath(fullDest));
7366         }
7368         return true;
7369         }
7371     virtual bool parse(Element *elem)
7372         {
7373         if (!parent.getAttribute(elem, "command", commandOpt))
7374             return false;
7375         if (!parent.getAttribute(elem, "template", templateFileOpt))
7376             return false;
7377         if (!parent.getAttribute(elem, "out", destPathOpt))
7378             return false;
7379             
7380         std::vector<Element *> children = elem->getChildren();
7381         for (unsigned int i=0 ; i<children.size() ; i++)
7382             {
7383             Element *child = children[i];
7384             String tagName = child->getName();
7385             if (tagName == "fileset")
7386                 {
7387                 if (!parseFileSet(child, parent, fileSet))
7388                     return false;
7389                 }
7390             }
7391         return true;
7392         }
7394 private:
7396     String  commandOpt;
7397     String  templateFileOpt;
7398     String  destPathOpt;
7399     FileSet fileSet;
7401 };
7404 /**
7405  *
7406  */
7407 class TaskDelete : public Task
7409 public:
7411     typedef enum
7412         {
7413         DEL_FILE,
7414         DEL_DIR,
7415         DEL_FILESET
7416         } DeleteType;
7418     TaskDelete(MakeBase &par) : Task(par)
7419         { 
7420         type        = TASK_DELETE;
7421         name        = "delete";
7422         delType     = DEL_FILE;
7423         }
7425     virtual ~TaskDelete()
7426         {}
7428     virtual bool execute()
7429         {
7430         String dirName   = parent.eval(dirNameOpt, ".");
7431         String fileName  = parent.eval(fileNameOpt, ".");
7432         bool verbose     = parent.evalBool(verboseOpt, false);
7433         bool quiet       = parent.evalBool(quietOpt, false);
7434         bool failOnError = parent.evalBool(failOnErrorOpt, true);
7435         struct stat finfo;
7436         switch (delType)
7437             {
7438             case DEL_FILE:
7439                 {
7440                 taskstatus("file: %s", fileName.c_str());
7441                 String fullName = parent.resolve(fileName);
7442                 char *fname = (char *)fullName.c_str();
7443                 if (!quiet && verbose)
7444                     taskstatus("path: %s", fname);
7445                 if (!removeFile(fullName)) return false;
7446                 return true;
7447                 }
7448             case DEL_DIR:
7449                 {
7450                 taskstatus("dir: %s", dirName.c_str());
7451                 String fullDir = parent.resolve(dirName);
7452                 if (!quiet && verbose)
7453                     taskstatus("path: %s", fullDir.c_str());
7454                 if (!removeDirectory(fullDir))
7455                     return false;
7456                 return true;
7457                 }
7458             }
7459         return true;
7460         }
7462     virtual bool parse(Element *elem)
7463         {
7464         if (!parent.getAttribute(elem, "file", fileNameOpt))
7465             return false;
7466         if (fileNameOpt.size() > 0)
7467             delType = DEL_FILE;
7468         if (!parent.getAttribute(elem, "dir", dirNameOpt))
7469             return false;
7470         if (dirNameOpt.size() > 0)
7471             delType = DEL_DIR;
7472         if (fileNameOpt.size()>0 && dirNameOpt.size()>0)
7473             {
7474             error("<delete> can have one attribute of file= or dir=");
7475             return false;
7476             }
7477         if (fileNameOpt.size()==0 && dirNameOpt.size()==0)
7478             {
7479             error("<delete> must have one attribute of file= or dir=");
7480             return false;
7481             }
7482         if (!parent.getAttribute(elem, "verbose", verboseOpt))
7483             return false;
7484         if (!parent.getAttribute(elem, "quiet", quietOpt))
7485             return false;
7486         if (!parent.getAttribute(elem, "failonerror", failOnErrorOpt))
7487             return false;
7488         return true;
7489         }
7491 private:
7493     int delType;
7494     String dirNameOpt;
7495     String fileNameOpt;
7496     String verboseOpt;
7497     String quietOpt;
7498     String failOnErrorOpt;
7499 };
7502 /**
7503  * Send a message to stdout
7504  */
7505 class TaskEcho : public Task
7507 public:
7509     TaskEcho(MakeBase &par) : Task(par)
7510         { type = TASK_ECHO; name = "echo"; }
7512     virtual ~TaskEcho()
7513         {}
7515     virtual bool execute()
7516         {
7517         //let message have priority over text
7518         String message = parent.eval(messageOpt, "");
7519         String text    = parent.eval(textOpt, "");
7520         if (message.size() > 0)
7521             {
7522             fprintf(stdout, "%s\n", message.c_str());
7523             }
7524         else if (text.size() > 0)
7525             {
7526             fprintf(stdout, "%s\n", text.c_str());
7527             }
7528         return true;
7529         }
7531     virtual bool parse(Element *elem)
7532         {
7533         if (!parent.getValue(elem, textOpt))
7534             return false;
7535         textOpt    = leftJustify(textOpt);
7536         if (!parent.getAttribute(elem, "message", messageOpt))
7537             return false;
7538         return true;
7539         }
7541 private:
7543     String messageOpt;
7544     String textOpt;
7545 };
7549 /**
7550  *
7551  */
7552 class TaskJar : public Task
7554 public:
7556     TaskJar(MakeBase &par) : Task(par)
7557         { type = TASK_JAR; name = "jar"; }
7559     virtual ~TaskJar()
7560         {}
7562     virtual bool execute()
7563         {
7564         String command  = parent.eval(commandOpt, "jar");
7565         String basedir  = parent.eval(basedirOpt, ".");
7566         String destfile = parent.eval(destfileOpt, ".");
7568         String cmd = command;
7569         cmd.append(" -cf ");
7570         cmd.append(destfile);
7571         cmd.append(" -C ");
7572         cmd.append(basedir);
7573         cmd.append(" .");
7575         String execCmd = cmd;
7577         String outString, errString;
7578         bool ret = executeCommand(execCmd.c_str(), "", outString, errString);
7579         if (!ret)
7580             {
7581             error("<jar> command '%s' failed :\n %s",
7582                                       execCmd.c_str(), errString.c_str());
7583             return false;
7584             }
7585         removeFromStatCache(getNativePath(destfile));
7586         return true;
7587         }
7589     virtual bool parse(Element *elem)
7590         {
7591         if (!parent.getAttribute(elem, "command", commandOpt))
7592             return false;
7593         if (!parent.getAttribute(elem, "basedir", basedirOpt))
7594             return false;
7595         if (!parent.getAttribute(elem, "destfile", destfileOpt))
7596             return false;
7597         if (basedirOpt.size() == 0 || destfileOpt.size() == 0)
7598             {
7599             error("<jar> required both basedir and destfile attributes to be set");
7600             return false;
7601             }
7602         return true;
7603         }
7605 private:
7607     String commandOpt;
7608     String basedirOpt;
7609     String destfileOpt;
7610 };
7613 /**
7614  *
7615  */
7616 class TaskJavac : public Task
7618 public:
7620     TaskJavac(MakeBase &par) : Task(par)
7621         { 
7622         type = TASK_JAVAC; name = "javac";
7623         }
7625     virtual ~TaskJavac()
7626         {}
7628     virtual bool execute()
7629         {
7630         String command  = parent.eval(commandOpt, "javac");
7631         String srcdir   = parent.eval(srcdirOpt, ".");
7632         String destdir  = parent.eval(destdirOpt, ".");
7633         String target   = parent.eval(targetOpt, "");
7635         std::vector<String> fileList;
7636         if (!listFiles(srcdir, "", fileList))
7637             {
7638             return false;
7639             }
7640         String cmd = command;
7641         cmd.append(" -d ");
7642         cmd.append(destdir);
7643         cmd.append(" -classpath ");
7644         cmd.append(destdir);
7645         cmd.append(" -sourcepath ");
7646         cmd.append(srcdir);
7647         cmd.append(" ");
7648         if (target.size()>0)
7649             {
7650             cmd.append(" -target ");
7651             cmd.append(target);
7652             cmd.append(" ");
7653             }
7654         String fname = "javalist.btool";
7655         FILE *f = fopen(fname.c_str(), "w");
7656         int count = 0;
7657         for (unsigned int i=0 ; i<fileList.size() ; i++)
7658             {
7659             String fname = fileList[i];
7660             String srcName = fname;
7661             if (fname.size()<6) //x.java
7662                 continue;
7663             if (fname.compare(fname.size()-5, 5, ".java") != 0)
7664                 continue;
7665             String baseName = fname.substr(0, fname.size()-5);
7666             String destName = baseName;
7667             destName.append(".class");
7669             String fullSrc = srcdir;
7670             fullSrc.append("/");
7671             fullSrc.append(fname);
7672             String fullDest = destdir;
7673             fullDest.append("/");
7674             fullDest.append(destName);
7675             //trace("fullsrc:%s fulldest:%s", fullSrc.c_str(), fullDest.c_str());
7676             if (!isNewerThan(fullSrc, fullDest))
7677                 continue;
7679             count++;
7680             fprintf(f, "%s\n", fullSrc.c_str());
7681             }
7682         fclose(f);
7683         if (!count)
7684             {
7685             taskstatus("nothing to do");
7686             return true;
7687             }
7689         taskstatus("compiling %d files", count);
7691         String execCmd = cmd;
7692         execCmd.append("@");
7693         execCmd.append(fname);
7695         String outString, errString;
7696         bool ret = executeCommand(execCmd.c_str(), "", outString, errString);
7697         if (!ret)
7698             {
7699             error("<javac> command '%s' failed :\n %s",
7700                                       execCmd.c_str(), errString.c_str());
7701             return false;
7702             }
7703         // TODO: 
7704         //removeFromStatCache(getNativePath(........));
7705         return true;
7706         }
7708     virtual bool parse(Element *elem)
7709         {
7710         if (!parent.getAttribute(elem, "command", commandOpt))
7711             return false;
7712         if (!parent.getAttribute(elem, "srcdir", srcdirOpt))
7713             return false;
7714         if (!parent.getAttribute(elem, "destdir", destdirOpt))
7715             return false;
7716         if (srcdirOpt.size() == 0 || destdirOpt.size() == 0)
7717             {
7718             error("<javac> required both srcdir and destdir attributes to be set");
7719             return false;
7720             }
7721         if (!parent.getAttribute(elem, "target", targetOpt))
7722             return false;
7723         return true;
7724         }
7726 private:
7728     String commandOpt;
7729     String srcdirOpt;
7730     String destdirOpt;
7731     String targetOpt;
7733 };
7736 /**
7737  *
7738  */
7739 class TaskLink : public Task
7741 public:
7743     TaskLink(MakeBase &par) : Task(par)
7744         {
7745         type = TASK_LINK; name = "link";
7746         }
7748     virtual ~TaskLink()
7749         {}
7751     virtual bool execute()
7752         {
7753         String  command        = parent.eval(commandOpt, "g++");
7754         String  fileName       = parent.eval(fileNameOpt, "");
7755         String  flags          = parent.eval(flagsOpt, "");
7756         String  libs           = parent.eval(libsOpt, "");
7757         bool    doStrip        = parent.evalBool(doStripOpt, false);
7758         String  symFileName    = parent.eval(symFileNameOpt, "");
7759         String  stripCommand   = parent.eval(stripCommandOpt, "strip");
7760         String  objcopyCommand = parent.eval(objcopyCommandOpt, "objcopy");
7762         if (!listFiles(parent, fileSet))
7763             return false;
7764         String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
7765         //trace("%d files in %s", fileSet.size(), fileSetDir.c_str());
7766         bool doit = false;
7767         String fullTarget = parent.resolve(fileName);
7768         String cmd = command;
7769         cmd.append(" -o ");
7770         cmd.append(fullTarget);
7771         cmd.append(" ");
7772         cmd.append(flags);
7773         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7774             {
7775             cmd.append(" ");
7776             String obj;
7777             if (fileSetDir.size()>0)
7778                 {
7779                 obj.append(fileSetDir);
7780                 obj.append("/");
7781                 }
7782             obj.append(fileSet[i]);
7783             String fullObj = parent.resolve(obj);
7784             String nativeFullObj = getNativePath(fullObj);
7785             cmd.append(nativeFullObj);
7786             //trace("link: tgt:%s obj:%s", fullTarget.c_str(),
7787             //          fullObj.c_str());
7788             if (isNewerThan(fullObj, fullTarget))
7789                 doit = true;
7790             }
7791         cmd.append(" ");
7792         cmd.append(libs);
7793         if (!doit)
7794             {
7795             //trace("link not needed");
7796             return true;
7797             }
7798         //trace("LINK cmd:%s", cmd.c_str());
7801         String outbuf, errbuf;
7802         if (!executeCommand(cmd.c_str(), "", outbuf, errbuf))
7803             {
7804             error("LINK problem: %s", errbuf.c_str());
7805             return false;
7806             }
7807         removeFromStatCache(getNativePath(fullTarget));
7809         if (symFileName.size()>0)
7810             {
7811             String symFullName = parent.resolve(symFileName);
7812             cmd = objcopyCommand;
7813             cmd.append(" --only-keep-debug ");
7814             cmd.append(getNativePath(fullTarget));
7815             cmd.append(" ");
7816             cmd.append(getNativePath(symFullName));
7817             if (!executeCommand(cmd, "", outbuf, errbuf))
7818                 {
7819                 error("<strip> symbol file failed : %s", errbuf.c_str());
7820                 return false;
7821                 }
7822             removeFromStatCache(getNativePath(symFullName));
7823             }
7824             
7825         if (doStrip)
7826             {
7827             cmd = stripCommand;
7828             cmd.append(" ");
7829             cmd.append(getNativePath(fullTarget));
7830             if (!executeCommand(cmd, "", outbuf, errbuf))
7831                {
7832                error("<strip> failed : %s", errbuf.c_str());
7833                return false;
7834                }
7835             removeFromStatCache(getNativePath(fullTarget));
7836             }
7838         return true;
7839         }
7841     virtual bool parse(Element *elem)
7842         {
7843         if (!parent.getAttribute(elem, "command", commandOpt))
7844             return false;
7845         if (!parent.getAttribute(elem, "objcopycommand", objcopyCommandOpt))
7846             return false;
7847         if (!parent.getAttribute(elem, "stripcommand", stripCommandOpt))
7848             return false;
7849         if (!parent.getAttribute(elem, "out", fileNameOpt))
7850             return false;
7851         if (!parent.getAttribute(elem, "strip", doStripOpt))
7852             return false;
7853         if (!parent.getAttribute(elem, "symfile", symFileNameOpt))
7854             return false;
7855             
7856         std::vector<Element *> children = elem->getChildren();
7857         for (unsigned int i=0 ; i<children.size() ; i++)
7858             {
7859             Element *child = children[i];
7860             String tagName = child->getName();
7861             if (tagName == "fileset")
7862                 {
7863                 if (!parseFileSet(child, parent, fileSet))
7864                     return false;
7865                 }
7866             else if (tagName == "flags")
7867                 {
7868                 if (!parent.getValue(child, flagsOpt))
7869                     return false;
7870                 flagsOpt = strip(flagsOpt);
7871                 }
7872             else if (tagName == "libs")
7873                 {
7874                 if (!parent.getValue(child, libsOpt))
7875                     return false;
7876                 libsOpt = strip(libsOpt);
7877                 }
7878             }
7879         return true;
7880         }
7882 private:
7884     FileSet fileSet;
7886     String  commandOpt;
7887     String  fileNameOpt;
7888     String  flagsOpt;
7889     String  libsOpt;
7890     String  doStripOpt;
7891     String  symFileNameOpt;
7892     String  stripCommandOpt;
7893     String  objcopyCommandOpt;
7895 };
7899 /**
7900  * Create a named file
7901  */
7902 class TaskMakeFile : public Task
7904 public:
7906     TaskMakeFile(MakeBase &par) : Task(par)
7907         { type = TASK_MAKEFILE; name = "makefile"; }
7909     virtual ~TaskMakeFile()
7910         {}
7912     virtual bool execute()
7913         {
7914         String fileName = parent.eval(fileNameOpt, "");
7915         String text     = parent.eval(textOpt, "");
7917         taskstatus("%s", fileName.c_str());
7918         String fullName = parent.resolve(fileName);
7919         if (!isNewerThan(parent.getURI().getPath(), fullName))
7920             {
7921             //trace("skipped <makefile>");
7922             return true;
7923             }
7924         String fullNative = getNativePath(fullName);
7925         //trace("fullName:%s", fullName.c_str());
7926         FILE *f = fopen(fullNative.c_str(), "w");
7927         if (!f)
7928             {
7929             error("<makefile> could not open %s for writing : %s",
7930                 fullName.c_str(), strerror(errno));
7931             return false;
7932             }
7933         for (unsigned int i=0 ; i<text.size() ; i++)
7934             fputc(text[i], f);
7935         fputc('\n', f);
7936         fclose(f);
7937         removeFromStatCache(fullNative);
7938         return true;
7939         }
7941     virtual bool parse(Element *elem)
7942         {
7943         if (!parent.getAttribute(elem, "file", fileNameOpt))
7944             return false;
7945         if (fileNameOpt.size() == 0)
7946             {
7947             error("<makefile> requires 'file=\"filename\"' attribute");
7948             return false;
7949             }
7950         if (!parent.getValue(elem, textOpt))
7951             return false;
7952         textOpt = leftJustify(textOpt);
7953         //trace("dirname:%s", dirName.c_str());
7954         return true;
7955         }
7957 private:
7959     String fileNameOpt;
7960     String textOpt;
7961 };
7965 /**
7966  * Create a named directory
7967  */
7968 class TaskMkDir : public Task
7970 public:
7972     TaskMkDir(MakeBase &par) : Task(par)
7973         { type = TASK_MKDIR; name = "mkdir"; }
7975     virtual ~TaskMkDir()
7976         {}
7978     virtual bool execute()
7979         {
7980         String dirName = parent.eval(dirNameOpt, ".");
7981         
7982         taskstatus("%s", dirName.c_str());
7983         String fullDir = parent.resolve(dirName);
7984         //trace("fullDir:%s", fullDir.c_str());
7985         if (!createDirectory(fullDir))
7986             return false;
7987         return true;
7988         }
7990     virtual bool parse(Element *elem)
7991         {
7992         if (!parent.getAttribute(elem, "dir", dirNameOpt))
7993             return false;
7994         if (dirNameOpt.size() == 0)
7995             {
7996             error("<mkdir> requires 'dir=\"dirname\"' attribute");
7997             return false;
7998             }
7999         return true;
8000         }
8002 private:
8004     String dirNameOpt;
8005 };
8009 /**
8010  * Create a named directory
8011  */
8012 class TaskMsgFmt: public Task
8014 public:
8016     TaskMsgFmt(MakeBase &par) : Task(par)
8017          { type = TASK_MSGFMT;  name = "msgfmt"; }
8019     virtual ~TaskMsgFmt()
8020         {}
8022     virtual bool execute()
8023         {
8024         String  command   = parent.eval(commandOpt, "msgfmt");
8025         String  toDirName = parent.eval(toDirNameOpt, ".");
8026         String  outName   = parent.eval(outNameOpt, "");
8027         bool    owndir    = parent.evalBool(owndirOpt, false);
8029         if (!listFiles(parent, fileSet))
8030             return false;
8031         String fileSetDir = fileSet.getDirectory();
8033         //trace("msgfmt: %d", fileSet.size());
8034         for (unsigned int i=0 ; i<fileSet.size() ; i++)
8035             {
8036             String fileName = fileSet[i];
8037             if (getSuffix(fileName) != "po")
8038                 continue;
8039             String sourcePath;
8040             if (fileSetDir.size()>0)
8041                 {
8042                 sourcePath.append(fileSetDir);
8043                 sourcePath.append("/");
8044                 }
8045             sourcePath.append(fileName);
8046             String fullSource = parent.resolve(sourcePath);
8048             String destPath;
8049             if (toDirName.size()>0)
8050                 {
8051                 destPath.append(toDirName);
8052                 destPath.append("/");
8053                 }
8054             if (owndir)
8055                 {
8056                 String subdir = fileName;
8057                 unsigned int pos = subdir.find_last_of('.');
8058                 if (pos != subdir.npos)
8059                     subdir = subdir.substr(0, pos);
8060                 destPath.append(subdir);
8061                 destPath.append("/");
8062                 }
8063             //Pick the output file name
8064             if (outName.size() > 0)
8065                 {
8066                 destPath.append(outName);
8067                 }
8068             else
8069                 {
8070                 destPath.append(fileName);
8071                 destPath[destPath.size()-2] = 'm';
8072                 }
8074             String fullDest = parent.resolve(destPath);
8076             if (!isNewerThan(fullSource, fullDest))
8077                 {
8078                 //trace("skip %s", fullSource.c_str());
8079                 continue;
8080                 }
8081                 
8082             String cmd = command;
8083             cmd.append(" ");
8084             cmd.append(fullSource);
8085             cmd.append(" -o ");
8086             cmd.append(fullDest);
8087             
8088             int pos = fullDest.find_last_of('/');
8089             if (pos>0)
8090                 {
8091                 String fullDestPath = fullDest.substr(0, pos);
8092                 if (!createDirectory(fullDestPath))
8093                     return false;
8094                 }
8098             String outString, errString;
8099             if (!executeCommand(cmd.c_str(), "", outString, errString))
8100                 {
8101                 error("<msgfmt> problem: %s", errString.c_str());
8102                 return false;
8103                 }
8104             removeFromStatCache(getNativePath(fullDest));
8105             }
8107         return true;
8108         }
8110     virtual bool parse(Element *elem)
8111         {
8112         if (!parent.getAttribute(elem, "command", commandOpt))
8113             return false;
8114         if (!parent.getAttribute(elem, "todir", toDirNameOpt))
8115             return false;
8116         if (!parent.getAttribute(elem, "out", outNameOpt))
8117             return false;
8118         if (!parent.getAttribute(elem, "owndir", owndirOpt))
8119             return false;
8120             
8121         std::vector<Element *> children = elem->getChildren();
8122         for (unsigned int i=0 ; i<children.size() ; i++)
8123             {
8124             Element *child = children[i];
8125             String tagName = child->getName();
8126             if (tagName == "fileset")
8127                 {
8128                 if (!parseFileSet(child, parent, fileSet))
8129                     return false;
8130                 }
8131             }
8132         return true;
8133         }
8135 private:
8137     FileSet fileSet;
8139     String  commandOpt;
8140     String  toDirNameOpt;
8141     String  outNameOpt;
8142     String  owndirOpt;
8144 };
8148 /**
8149  *  Perform a Package-Config query similar to pkg-config
8150  */
8151 class TaskPkgConfig : public Task
8153 public:
8155     typedef enum
8156         {
8157         PKG_CONFIG_QUERY_CFLAGS,
8158         PKG_CONFIG_QUERY_LIBS,
8159         PKG_CONFIG_QUERY_ALL
8160         } QueryTypes;
8162     TaskPkgConfig(MakeBase &par) : Task(par)
8163         {
8164         type = TASK_PKG_CONFIG;
8165         name = "pkg-config";
8166         }
8168     virtual ~TaskPkgConfig()
8169         {}
8171     virtual bool execute()
8172         {
8173         String pkgName       = parent.eval(pkgNameOpt,      "");
8174         String prefix        = parent.eval(prefixOpt,       "");
8175         String propName      = parent.eval(propNameOpt,     "");
8176         String pkgConfigPath = parent.eval(pkgConfigPathOpt,"");
8177         String query         = parent.eval(queryOpt,        "all");
8179         String path = parent.resolve(pkgConfigPath);
8180         PkgConfig pkgconfig;
8181         pkgconfig.setPath(path);
8182         pkgconfig.setPrefix(prefix);
8183         if (!pkgconfig.query(pkgName))
8184             {
8185             error("<pkg-config> query failed for '%s", name.c_str());
8186             return false;
8187             }
8188             
8189         String val = "";
8190         if (query == "cflags")
8191             val = pkgconfig.getCflags();
8192         else if (query == "libs")
8193             val =pkgconfig.getLibs();
8194         else if (query == "all")
8195             val = pkgconfig.getAll();
8196         else
8197             {
8198             error("<pkg-config> unhandled query : %s", query.c_str());
8199             return false;
8200             }
8201         taskstatus("property %s = '%s'", propName.c_str(), val.c_str());
8202         parent.setProperty(propName, val);
8203         return true;
8204         }
8206     virtual bool parse(Element *elem)
8207         {
8208         //# NAME
8209         if (!parent.getAttribute(elem, "name", pkgNameOpt))
8210             return false;
8211         if (pkgNameOpt.size()==0)
8212             {
8213             error("<pkg-config> requires 'name=\"package\"' attribute");
8214             return false;
8215             }
8217         //# PROPERTY
8218         if (!parent.getAttribute(elem, "property", propNameOpt))
8219             return false;
8220         if (propNameOpt.size()==0)
8221             {
8222             error("<pkg-config> requires 'property=\"name\"' attribute");
8223             return false;
8224             }
8225         //# PATH
8226         if (!parent.getAttribute(elem, "path", pkgConfigPathOpt))
8227             return false;
8228         //# PREFIX
8229         if (!parent.getAttribute(elem, "prefix", prefixOpt))
8230             return false;
8231         //# QUERY
8232         if (!parent.getAttribute(elem, "query", queryOpt))
8233             return false;
8235         return true;
8236         }
8238 private:
8240     String queryOpt;
8241     String pkgNameOpt;
8242     String prefixOpt;
8243     String propNameOpt;
8244     String pkgConfigPathOpt;
8246 };
8253 /**
8254  *  Process an archive to allow random access
8255  */
8256 class TaskRanlib : public Task
8258 public:
8260     TaskRanlib(MakeBase &par) : Task(par)
8261         { type = TASK_RANLIB; name = "ranlib"; }
8263     virtual ~TaskRanlib()
8264         {}
8266     virtual bool execute()
8267         {
8268         String fileName = parent.eval(fileNameOpt, "");
8269         String command  = parent.eval(commandOpt, "ranlib");
8271         String fullName = parent.resolve(fileName);
8272         //trace("fullDir:%s", fullDir.c_str());
8273         String cmd = command;
8274         cmd.append(" ");
8275         cmd.append(fullName);
8276         String outbuf, errbuf;
8277         if (!executeCommand(cmd, "", outbuf, errbuf))
8278             return false;
8279         // TODO:
8280         //removeFromStatCache(getNativePath(fullDest));
8281         return true;
8282         }
8284     virtual bool parse(Element *elem)
8285         {
8286         if (!parent.getAttribute(elem, "command", commandOpt))
8287             return false;
8288         if (!parent.getAttribute(elem, "file", fileNameOpt))
8289             return false;
8290         if (fileNameOpt.size() == 0)
8291             {
8292             error("<ranlib> requires 'file=\"fileNname\"' attribute");
8293             return false;
8294             }
8295         return true;
8296         }
8298 private:
8300     String fileNameOpt;
8301     String commandOpt;
8302 };
8306 /**
8307  * Compile a resource file into a binary object
8308  */
8309 class TaskRC : public Task
8311 public:
8313     TaskRC(MakeBase &par) : Task(par)
8314         { type = TASK_RC; name = "rc"; }
8316     virtual ~TaskRC()
8317         {}
8319     virtual bool execute()
8320         {
8321         String command  = parent.eval(commandOpt,  "windres");
8322         String flags    = parent.eval(flagsOpt,    "");
8323         String fileName = parent.eval(fileNameOpt, "");
8324         String outName  = parent.eval(outNameOpt,  "");
8326         String fullFile = parent.resolve(fileName);
8327         String fullOut  = parent.resolve(outName);
8328         if (!isNewerThan(fullFile, fullOut))
8329             return true;
8330         String cmd = command;
8331         cmd.append(" -o ");
8332         cmd.append(fullOut);
8333         cmd.append(" ");
8334         cmd.append(flags);
8335         cmd.append(" ");
8336         cmd.append(fullFile);
8338         String outString, errString;
8339         if (!executeCommand(cmd.c_str(), "", outString, errString))
8340             {
8341             error("RC problem: %s", errString.c_str());
8342             return false;
8343             }
8344         removeFromStatCache(getNativePath(fullOut));
8345         return true;
8346         }
8348     virtual bool parse(Element *elem)
8349         {
8350         if (!parent.getAttribute(elem, "command", commandOpt))
8351             return false;
8352         if (!parent.getAttribute(elem, "file", fileNameOpt))
8353             return false;
8354         if (!parent.getAttribute(elem, "out", outNameOpt))
8355             return false;
8356         std::vector<Element *> children = elem->getChildren();
8357         for (unsigned int i=0 ; i<children.size() ; i++)
8358             {
8359             Element *child = children[i];
8360             String tagName = child->getName();
8361             if (tagName == "flags")
8362                 {
8363                 if (!parent.getValue(child, flagsOpt))
8364                     return false;
8365                 }
8366             }
8367         return true;
8368         }
8370 private:
8372     String commandOpt;
8373     String flagsOpt;
8374     String fileNameOpt;
8375     String outNameOpt;
8377 };
8381 /**
8382  *  Collect .o's into a .so or DLL
8383  */
8384 class TaskSharedLib : public Task
8386 public:
8388     TaskSharedLib(MakeBase &par) : Task(par)
8389         { type = TASK_SHAREDLIB; name = "dll"; }
8391     virtual ~TaskSharedLib()
8392         {}
8394     virtual bool execute()
8395         {
8396         String command     = parent.eval(commandOpt, "dllwrap");
8397         String fileName    = parent.eval(fileNameOpt, "");
8398         String defFileName = parent.eval(defFileNameOpt, "");
8399         String impFileName = parent.eval(impFileNameOpt, "");
8400         String libs        = parent.eval(libsOpt, "");
8402         //trace("###########HERE %d", fileSet.size());
8403         bool doit = false;
8404         
8405         String fullOut = parent.resolve(fileName);
8406         //trace("ar fullout: %s", fullOut.c_str());
8407         
8408         if (!listFiles(parent, fileSet))
8409             return false;
8410         String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
8412         for (unsigned int i=0 ; i<fileSet.size() ; i++)
8413             {
8414             String fname;
8415             if (fileSetDir.size()>0)
8416                 {
8417                 fname.append(fileSetDir);
8418                 fname.append("/");
8419                 }
8420             fname.append(fileSet[i]);
8421             String fullName = parent.resolve(fname);
8422             //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
8423             if (isNewerThan(fullName, fullOut))
8424                 doit = true;
8425             }
8426         //trace("Needs it:%d", doit);
8427         if (!doit)
8428             {
8429             return true;
8430             }
8432         String cmd = "dllwrap";
8433         cmd.append(" -o ");
8434         cmd.append(fullOut);
8435         if (defFileName.size()>0)
8436             {
8437             cmd.append(" --def ");
8438             cmd.append(defFileName);
8439             cmd.append(" ");
8440             }
8441         if (impFileName.size()>0)
8442             {
8443             cmd.append(" --implib ");
8444             cmd.append(impFileName);
8445             cmd.append(" ");
8446             }
8447         for (unsigned int i=0 ; i<fileSet.size() ; i++)
8448             {
8449             String fname;
8450             if (fileSetDir.size()>0)
8451                 {
8452                 fname.append(fileSetDir);
8453                 fname.append("/");
8454                 }
8455             fname.append(fileSet[i]);
8456             String fullName = parent.resolve(fname);
8458             cmd.append(" ");
8459             cmd.append(fullName);
8460             }
8461         cmd.append(" ");
8462         cmd.append(libs);
8464         String outString, errString;
8465         if (!executeCommand(cmd.c_str(), "", outString, errString))
8466             {
8467             error("<sharedlib> problem: %s", errString.c_str());
8468             return false;
8469             }
8470         removeFromStatCache(getNativePath(fullOut));
8471         return true;
8472         }
8474     virtual bool parse(Element *elem)
8475         {
8476         if (!parent.getAttribute(elem, "command", commandOpt))
8477             return false;
8478         if (!parent.getAttribute(elem, "file", fileNameOpt))
8479             return false;
8480         if (!parent.getAttribute(elem, "import", impFileNameOpt))
8481             return false;
8482         if (!parent.getAttribute(elem, "def", defFileNameOpt))
8483             return false;
8484             
8485         std::vector<Element *> children = elem->getChildren();
8486         for (unsigned int i=0 ; i<children.size() ; i++)
8487             {
8488             Element *child = children[i];
8489             String tagName = child->getName();
8490             if (tagName == "fileset")
8491                 {
8492                 if (!parseFileSet(child, parent, fileSet))
8493                     return false;
8494                 }
8495             else if (tagName == "libs")
8496                 {
8497                 if (!parent.getValue(child, libsOpt))
8498                     return false;
8499                 libsOpt = strip(libsOpt);
8500                 }
8501             }
8502         return true;
8503         }
8505 private:
8507     FileSet fileSet;
8509     String commandOpt;
8510     String fileNameOpt;
8511     String defFileNameOpt;
8512     String impFileNameOpt;
8513     String libsOpt;
8515 };
8519 /**
8520  * Run the "ar" command to archive .o's into a .a
8521  */
8522 class TaskStaticLib : public Task
8524 public:
8526     TaskStaticLib(MakeBase &par) : Task(par)
8527         { type = TASK_STATICLIB; name = "staticlib"; }
8529     virtual ~TaskStaticLib()
8530         {}
8532     virtual bool execute()
8533         {
8534         String command = parent.eval(commandOpt, "ar crv");
8535         String fileName = parent.eval(fileNameOpt, "");
8537         bool doit = false;
8538         
8539         String fullOut = parent.resolve(fileName);
8540         //trace("ar fullout: %s", fullOut.c_str());
8541         
8542         if (!listFiles(parent, fileSet))
8543             return false;
8544         String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
8545         //trace("###########HERE %s", fileSetDir.c_str());
8547         for (unsigned int i=0 ; i<fileSet.size() ; i++)
8548             {
8549             String fname;
8550             if (fileSetDir.size()>0)
8551                 {
8552                 fname.append(fileSetDir);
8553                 fname.append("/");
8554                 }
8555             fname.append(fileSet[i]);
8556             String fullName = parent.resolve(fname);
8557             //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
8558             if (isNewerThan(fullName, fullOut))
8559                 doit = true;
8560             }
8561         //trace("Needs it:%d", doit);
8562         if (!doit)
8563             {
8564             return true;
8565             }
8567         String cmd = command;
8568         cmd.append(" ");
8569         cmd.append(fullOut);
8570         for (unsigned int i=0 ; i<fileSet.size() ; i++)
8571             {
8572             String fname;
8573             if (fileSetDir.size()>0)
8574                 {
8575                 fname.append(fileSetDir);
8576                 fname.append("/");
8577                 }
8578             fname.append(fileSet[i]);
8579             String fullName = parent.resolve(fname);
8581             cmd.append(" ");
8582             cmd.append(fullName);
8583             }
8585         String outString, errString;
8586         if (!executeCommand(cmd.c_str(), "", outString, errString))
8587             {
8588             error("<staticlib> problem: %s", errString.c_str());
8589             return false;
8590             }
8591         removeFromStatCache(getNativePath(fullOut));
8592         return true;
8593         }
8596     virtual bool parse(Element *elem)
8597         {
8598         if (!parent.getAttribute(elem, "command", commandOpt))
8599             return false;
8600         if (!parent.getAttribute(elem, "file", fileNameOpt))
8601             return false;
8602             
8603         std::vector<Element *> children = elem->getChildren();
8604         for (unsigned int i=0 ; i<children.size() ; i++)
8605             {
8606             Element *child = children[i];
8607             String tagName = child->getName();
8608             if (tagName == "fileset")
8609                 {
8610                 if (!parseFileSet(child, parent, fileSet))
8611                     return false;
8612                 }
8613             }
8614         return true;
8615         }
8617 private:
8619     FileSet fileSet;
8621     String commandOpt;
8622     String fileNameOpt;
8624 };
8629 /**
8630  * Strip an executable
8631  */
8632 class TaskStrip : public Task
8634 public:
8636     TaskStrip(MakeBase &par) : Task(par)
8637         { type = TASK_STRIP; name = "strip"; }
8639     virtual ~TaskStrip()
8640         {}
8642     virtual bool execute()
8643         {
8644         String command     = parent.eval(commandOpt, "strip");
8645         String fileName    = parent.eval(fileNameOpt, "");
8646         String symFileName = parent.eval(symFileNameOpt, "");
8648         String fullName = parent.resolve(fileName);
8649         //trace("fullDir:%s", fullDir.c_str());
8650         String cmd;
8651         String outbuf, errbuf;
8653         if (symFileName.size()>0)
8654             {
8655             String symFullName = parent.resolve(symFileName);
8656             cmd = "objcopy --only-keep-debug ";
8657             cmd.append(getNativePath(fullName));
8658             cmd.append(" ");
8659             cmd.append(getNativePath(symFullName));
8660             if (!executeCommand(cmd, "", outbuf, errbuf))
8661                 {
8662                 error("<strip> symbol file failed : %s", errbuf.c_str());
8663                 return false;
8664                 }
8665             }
8666             
8667         cmd = command;
8668         cmd.append(getNativePath(fullName));
8669         if (!executeCommand(cmd, "", outbuf, errbuf))
8670             {
8671             error("<strip> failed : %s", errbuf.c_str());
8672             return false;
8673             }
8674         removeFromStatCache(getNativePath(fullName));
8675         return true;
8676         }
8678     virtual bool parse(Element *elem)
8679         {
8680         if (!parent.getAttribute(elem, "command", commandOpt))
8681             return false;
8682         if (!parent.getAttribute(elem, "file", fileNameOpt))
8683             return false;
8684         if (!parent.getAttribute(elem, "symfile", symFileNameOpt))
8685             return false;
8686         if (fileNameOpt.size() == 0)
8687             {
8688             error("<strip> requires 'file=\"fileName\"' attribute");
8689             return false;
8690             }
8691         return true;
8692         }
8694 private:
8696     String commandOpt;
8697     String fileNameOpt;
8698     String symFileNameOpt;
8699 };
8702 /**
8703  *
8704  */
8705 class TaskTouch : public Task
8707 public:
8709     TaskTouch(MakeBase &par) : Task(par)
8710         { type = TASK_TOUCH; name = "touch"; }
8712     virtual ~TaskTouch()
8713         {}
8715     virtual bool execute()
8716         {
8717         String fileName = parent.eval(fileNameOpt, "");
8719         String fullName = parent.resolve(fileName);
8720         String nativeFile = getNativePath(fullName);
8721         if (!isRegularFile(fullName) && !isDirectory(fullName))
8722             {            
8723             // S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH
8724             int ret = creat(nativeFile.c_str(), 0666);
8725             if (ret != 0) 
8726                 {
8727                 error("<touch> could not create '%s' : %s",
8728                     nativeFile.c_str(), strerror(ret));
8729                 return false;
8730                 }
8731             return true;
8732             }
8733         int ret = utime(nativeFile.c_str(), (struct utimbuf *)0);
8734         if (ret != 0)
8735             {
8736             error("<touch> could not update the modification time for '%s' : %s",
8737                 nativeFile.c_str(), strerror(ret));
8738             return false;
8739             }
8740         removeFromStatCache(nativeFile);
8741         return true;
8742         }
8744     virtual bool parse(Element *elem)
8745         {
8746         //trace("touch parse");
8747         if (!parent.getAttribute(elem, "file", fileNameOpt))
8748             return false;
8749         if (fileNameOpt.size() == 0)
8750             {
8751             error("<touch> requires 'file=\"fileName\"' attribute");
8752             return false;
8753             }
8754         return true;
8755         }
8757     String fileNameOpt;
8758 };
8761 /**
8762  *
8763  */
8764 class TaskTstamp : public Task
8766 public:
8768     TaskTstamp(MakeBase &par) : Task(par)
8769         { type = TASK_TSTAMP; name = "tstamp"; }
8771     virtual ~TaskTstamp()
8772         {}
8774     virtual bool execute()
8775         {
8776         return true;
8777         }
8779     virtual bool parse(Element *elem)
8780         {
8781         //trace("tstamp parse");
8782         return true;
8783         }
8784 };
8788 /**
8789  *
8790  */
8791 Task *Task::createTask(Element *elem, int lineNr)
8793     String tagName = elem->getName();
8794     //trace("task:%s", tagName.c_str());
8795     Task *task = NULL;
8796     if (tagName == "cc")
8797         task = new TaskCC(parent);
8798     else if (tagName == "copy")
8799         task = new TaskCopy(parent);
8800     else if (tagName == "cxxtestpart")
8801         task = new TaskCxxTestPart(parent);
8802     else if (tagName == "cxxtestroot")
8803         task = new TaskCxxTestRoot(parent);
8804     else if (tagName == "delete")
8805         task = new TaskDelete(parent);
8806     else if (tagName == "echo")
8807         task = new TaskEcho(parent);
8808     else if (tagName == "jar")
8809         task = new TaskJar(parent);
8810     else if (tagName == "javac")
8811         task = new TaskJavac(parent);
8812     else if (tagName == "link")
8813         task = new TaskLink(parent);
8814     else if (tagName == "makefile")
8815         task = new TaskMakeFile(parent);
8816     else if (tagName == "mkdir")
8817         task = new TaskMkDir(parent);
8818     else if (tagName == "msgfmt")
8819         task = new TaskMsgFmt(parent);
8820     else if (tagName == "pkg-config")
8821         task = new TaskPkgConfig(parent);
8822     else if (tagName == "ranlib")
8823         task = new TaskRanlib(parent);
8824     else if (tagName == "rc")
8825         task = new TaskRC(parent);
8826     else if (tagName == "sharedlib")
8827         task = new TaskSharedLib(parent);
8828     else if (tagName == "staticlib")
8829         task = new TaskStaticLib(parent);
8830     else if (tagName == "strip")
8831         task = new TaskStrip(parent);
8832     else if (tagName == "touch")
8833         task = new TaskTouch(parent);
8834     else if (tagName == "tstamp")
8835         task = new TaskTstamp(parent);
8836     else
8837         {
8838         error("Unknown task '%s'", tagName.c_str());
8839         return NULL;
8840         }
8842     task->setLine(lineNr);
8844     if (!task->parse(elem))
8845         {
8846         delete task;
8847         return NULL;
8848         }
8849     return task;
8854 //########################################################################
8855 //# T A R G E T
8856 //########################################################################
8858 /**
8859  *
8860  */
8861 class Target : public MakeBase
8864 public:
8866     /**
8867      *
8868      */
8869     Target(Make &par) : parent(par)
8870         { init(); }
8872     /**
8873      *
8874      */
8875     Target(const Target &other) : parent(other.parent)
8876         { init(); assign(other); }
8878     /**
8879      *
8880      */
8881     Target &operator=(const Target &other)
8882         { init(); assign(other); return *this; }
8884     /**
8885      *
8886      */
8887     virtual ~Target()
8888         { cleanup() ; }
8891     /**
8892      *
8893      */
8894     virtual Make &getParent()
8895         { return parent; }
8897     /**
8898      *
8899      */
8900     virtual String getName()
8901         { return name; }
8903     /**
8904      *
8905      */
8906     virtual void setName(const String &val)
8907         { name = val; }
8909     /**
8910      *
8911      */
8912     virtual String getDescription()
8913         { return description; }
8915     /**
8916      *
8917      */
8918     virtual void setDescription(const String &val)
8919         { description = val; }
8921     /**
8922      *
8923      */
8924     virtual void addDependency(const String &val)
8925         { deps.push_back(val); }
8927     /**
8928      *
8929      */
8930     virtual void parseDependencies(const String &val)
8931         { deps = tokenize(val, ", "); }
8933     /**
8934      *
8935      */
8936     virtual std::vector<String> &getDependencies()
8937         { return deps; }
8939     /**
8940      *
8941      */
8942     virtual String getIf()
8943         { return ifVar; }
8945     /**
8946      *
8947      */
8948     virtual void setIf(const String &val)
8949         { ifVar = val; }
8951     /**
8952      *
8953      */
8954     virtual String getUnless()
8955         { return unlessVar; }
8957     /**
8958      *
8959      */
8960     virtual void setUnless(const String &val)
8961         { unlessVar = val; }
8963     /**
8964      *
8965      */
8966     virtual void addTask(Task *val)
8967         { tasks.push_back(val); }
8969     /**
8970      *
8971      */
8972     virtual std::vector<Task *> &getTasks()
8973         { return tasks; }
8975 private:
8977     void init()
8978         {
8979         }
8981     void cleanup()
8982         {
8983         tasks.clear();
8984         }
8986     void assign(const Target &other)
8987         {
8988         //parent      = other.parent;
8989         name        = other.name;
8990         description = other.description;
8991         ifVar       = other.ifVar;
8992         unlessVar   = other.unlessVar;
8993         deps        = other.deps;
8994         tasks       = other.tasks;
8995         }
8997     Make &parent;
8999     String name;
9001     String description;
9003     String ifVar;
9005     String unlessVar;
9007     std::vector<String> deps;
9009     std::vector<Task *> tasks;
9011 };
9020 //########################################################################
9021 //# M A K E
9022 //########################################################################
9025 /**
9026  *
9027  */
9028 class Make : public MakeBase
9031 public:
9033     /**
9034      *
9035      */
9036     Make()
9037         { init(); }
9039     /**
9040      *
9041      */
9042     Make(const Make &other)
9043         { assign(other); }
9045     /**
9046      *
9047      */
9048     Make &operator=(const Make &other)
9049         { assign(other); return *this; }
9051     /**
9052      *
9053      */
9054     virtual ~Make()
9055         { cleanup(); }
9057     /**
9058      *
9059      */
9060     virtual std::map<String, Target> &getTargets()
9061         { return targets; }
9064     /**
9065      *
9066      */
9067     virtual String version()
9068         { return BUILDTOOL_VERSION; }
9070     /**
9071      * Overload a <property>
9072      */
9073     virtual bool specifyProperty(const String &name,
9074                                  const String &value);
9076     /**
9077      *
9078      */
9079     virtual bool run();
9081     /**
9082      *
9083      */
9084     virtual bool run(const String &target);
9088 private:
9090     /**
9091      *
9092      */
9093     void init();
9095     /**
9096      *
9097      */
9098     void cleanup();
9100     /**
9101      *
9102      */
9103     void assign(const Make &other);
9105     /**
9106      *
9107      */
9108     bool executeTask(Task &task);
9111     /**
9112      *
9113      */
9114     bool executeTarget(Target &target,
9115              std::set<String> &targetsCompleted);
9118     /**
9119      *
9120      */
9121     bool execute();
9123     /**
9124      *
9125      */
9126     bool checkTargetDependencies(Target &prop,
9127                     std::vector<String> &depList);
9129     /**
9130      *
9131      */
9132     bool parsePropertyFile(const String &fileName,
9133                            const String &prefix);
9135     /**
9136      *
9137      */
9138     bool parseProperty(Element *elem);
9140     /**
9141      *
9142      */
9143     bool parseFile();
9145     /**
9146      *
9147      */
9148     std::vector<String> glob(const String &pattern);
9151     //###############
9152     //# Fields
9153     //###############
9155     String projectName;
9157     String currentTarget;
9159     String defaultTarget;
9161     String specifiedTarget;
9163     String baseDir;
9165     String description;
9166     
9167     //std::vector<Property> properties;
9168     
9169     std::map<String, Target> targets;
9171     std::vector<Task *> allTasks;
9172     
9173     std::map<String, String> specifiedProperties;
9175 };
9178 //########################################################################
9179 //# C L A S S  M A I N T E N A N C E
9180 //########################################################################
9182 /**
9183  *
9184  */
9185 void Make::init()
9187     uri             = "build.xml";
9188     projectName     = "";
9189     currentTarget   = "";
9190     defaultTarget   = "";
9191     specifiedTarget = "";
9192     baseDir         = "";
9193     description     = "";
9194     envPrefix       = "env.";
9195     pcPrefix        = "pc.";
9196     pccPrefix       = "pcc.";
9197     pclPrefix       = "pcl.";
9198     properties.clear();
9199     for (unsigned int i = 0 ; i < allTasks.size() ; i++)
9200         delete allTasks[i];
9201     allTasks.clear();
9206 /**
9207  *
9208  */
9209 void Make::cleanup()
9211     for (unsigned int i = 0 ; i < allTasks.size() ; i++)
9212         delete allTasks[i];
9213     allTasks.clear();
9218 /**
9219  *
9220  */
9221 void Make::assign(const Make &other)
9223     uri              = other.uri;
9224     projectName      = other.projectName;
9225     currentTarget    = other.currentTarget;
9226     defaultTarget    = other.defaultTarget;
9227     specifiedTarget  = other.specifiedTarget;
9228     baseDir          = other.baseDir;
9229     description      = other.description;
9230     properties       = other.properties;
9235 //########################################################################
9236 //# U T I L I T Y    T A S K S
9237 //########################################################################
9239 /**
9240  *  Perform a file globbing
9241  */
9242 std::vector<String> Make::glob(const String &pattern)
9244     std::vector<String> res;
9245     return res;
9249 //########################################################################
9250 //# P U B L I C    A P I
9251 //########################################################################
9255 /**
9256  *
9257  */
9258 bool Make::executeTarget(Target &target,
9259              std::set<String> &targetsCompleted)
9262     String name = target.getName();
9264     //First get any dependencies for this target
9265     std::vector<String> deps = target.getDependencies();
9266     for (unsigned int i=0 ; i<deps.size() ; i++)
9267         {
9268         String dep = deps[i];
9269         //Did we do it already?  Skip
9270         if (targetsCompleted.find(dep)!=targetsCompleted.end())
9271             continue;
9272             
9273         std::map<String, Target> &tgts =
9274                target.getParent().getTargets();
9275         std::map<String, Target>::iterator iter =
9276                tgts.find(dep);
9277         if (iter == tgts.end())
9278             {
9279             error("Target '%s' dependency '%s' not found",
9280                       name.c_str(),  dep.c_str());
9281             return false;
9282             }
9283         Target depTarget = iter->second;
9284         if (!executeTarget(depTarget, targetsCompleted))
9285             {
9286             return false;
9287             }
9288         }
9290     status("##### Target : %s\n##### %s", name.c_str(),
9291             target.getDescription().c_str());
9293     //Now let's do the tasks
9294     std::vector<Task *> &tasks = target.getTasks();
9295     for (unsigned int i=0 ; i<tasks.size() ; i++)
9296         {
9297         Task *task = tasks[i];
9298         status("--- %s / %s", name.c_str(), task->getName().c_str());
9299         if (!task->execute())
9300             {
9301             return false;
9302             }
9303         }
9304         
9305     targetsCompleted.insert(name);
9306     
9307     return true;
9312 /**
9313  *  Main execute() method.  Start here and work
9314  *  up the dependency tree 
9315  */
9316 bool Make::execute()
9318     status("######## EXECUTE");
9320     //Determine initial target
9321     if (specifiedTarget.size()>0)
9322         {
9323         currentTarget = specifiedTarget;
9324         }
9325     else if (defaultTarget.size()>0)
9326         {
9327         currentTarget = defaultTarget;
9328         }
9329     else
9330         {
9331         error("execute: no specified or default target requested");
9332         return false;
9333         }
9335     std::map<String, Target>::iterator iter =
9336                targets.find(currentTarget);
9337     if (iter == targets.end())
9338         {
9339         error("Initial target '%s' not found",
9340                  currentTarget.c_str());
9341         return false;
9342         }
9343         
9344     //Now run
9345     Target target = iter->second;
9346     std::set<String> targetsCompleted;
9347     if (!executeTarget(target, targetsCompleted))
9348         {
9349         return false;
9350         }
9352     status("######## EXECUTE COMPLETE");
9353     return true;
9359 /**
9360  *
9361  */
9362 bool Make::checkTargetDependencies(Target &target, 
9363                             std::vector<String> &depList)
9365     String tgtName = target.getName().c_str();
9366     depList.push_back(tgtName);
9368     std::vector<String> deps = target.getDependencies();
9369     for (unsigned int i=0 ; i<deps.size() ; i++)
9370         {
9371         String dep = deps[i];
9372         //First thing entered was the starting Target
9373         if (dep == depList[0])
9374             {
9375             error("Circular dependency '%s' found at '%s'",
9376                       dep.c_str(), tgtName.c_str());
9377             std::vector<String>::iterator diter;
9378             for (diter=depList.begin() ; diter!=depList.end() ; diter++)
9379                 {
9380                 error("  %s", diter->c_str());
9381                 }
9382             return false;
9383             }
9385         std::map<String, Target> &tgts =
9386                   target.getParent().getTargets();
9387         std::map<String, Target>::iterator titer = tgts.find(dep);
9388         if (titer == tgts.end())
9389             {
9390             error("Target '%s' dependency '%s' not found",
9391                       tgtName.c_str(), dep.c_str());
9392             return false;
9393             }
9394         if (!checkTargetDependencies(titer->second, depList))
9395             {
9396             return false;
9397             }
9398         }
9399     return true;
9406 static int getword(int pos, const String &inbuf, String &result)
9408     int p = pos;
9409     int len = (int)inbuf.size();
9410     String val;
9411     while (p < len)
9412         {
9413         char ch = inbuf[p];
9414         if (!isalnum(ch) && ch!='.' && ch!='_')
9415             break;
9416         val.push_back(ch);
9417         p++;
9418         }
9419     result = val;
9420     return p;
9426 /**
9427  *
9428  */
9429 bool Make::parsePropertyFile(const String &fileName,
9430                              const String &prefix)
9432     FILE *f = fopen(fileName.c_str(), "r");
9433     if (!f)
9434         {
9435         error("could not open property file %s", fileName.c_str());
9436         return false;
9437         }
9438     int linenr = 0;
9439     while (!feof(f))
9440         {
9441         char buf[256];
9442         if (!fgets(buf, 255, f))
9443             break;
9444         linenr++;
9445         String s = buf;
9446         s = trim(s);
9447         int len = s.size();
9448         if (len == 0)
9449             continue;
9450         if (s[0] == '#')
9451             continue;
9452         String key;
9453         String val;
9454         int p = 0;
9455         int p2 = getword(p, s, key);
9456         if (p2 <= p)
9457             {
9458             error("property file %s, line %d: expected keyword",
9459                     fileName.c_str(), linenr);
9460             return false;
9461             }
9462         if (prefix.size() > 0)
9463             {
9464             key.insert(0, prefix);
9465             }
9467         //skip whitespace
9468         for (p=p2 ; p<len ; p++)
9469             if (!isspace(s[p]))
9470                 break;
9472         if (p>=len || s[p]!='=')
9473             {
9474             error("property file %s, line %d: expected '='",
9475                     fileName.c_str(), linenr);
9476             return false;
9477             }
9478         p++;
9480         //skip whitespace
9481         for ( ; p<len ; p++)
9482             if (!isspace(s[p]))
9483                 break;
9485         /* This way expects a word after the =
9486         p2 = getword(p, s, val);
9487         if (p2 <= p)
9488             {
9489             error("property file %s, line %d: expected value",
9490                     fileName.c_str(), linenr);
9491             return false;
9492             }
9493         */
9494         // This way gets the rest of the line after the =
9495         if (p>=len)
9496             {
9497             error("property file %s, line %d: expected value",
9498                     fileName.c_str(), linenr);
9499             return false;
9500             }
9501         val = s.substr(p);
9502         if (key.size()==0)
9503             continue;
9504         //allow property to be set, even if val=""
9506         //trace("key:'%s' val:'%s'", key.c_str(), val.c_str());
9507         //See if we wanted to overload this property
9508         std::map<String, String>::iterator iter =
9509             specifiedProperties.find(key);
9510         if (iter!=specifiedProperties.end())
9511             {
9512             val = iter->second;
9513             status("overloading property '%s' = '%s'",
9514                    key.c_str(), val.c_str());
9515             }
9516         properties[key] = val;
9517         }
9518     fclose(f);
9519     return true;
9525 /**
9526  *
9527  */
9528 bool Make::parseProperty(Element *elem)
9530     std::vector<Attribute> &attrs = elem->getAttributes();
9531     for (unsigned int i=0 ; i<attrs.size() ; i++)
9532         {
9533         String attrName = attrs[i].getName();
9534         String attrVal  = attrs[i].getValue();
9536         if (attrName == "name")
9537             {
9538             String val;
9539             if (!getAttribute(elem, "value", val))
9540                 return false;
9541             if (val.size() > 0)
9542                 {
9543                 properties[attrVal] = val;
9544                 }
9545             else
9546                 {
9547                 if (!getAttribute(elem, "location", val))
9548                     return false;
9549                 //let the property exist, even if not defined
9550                 properties[attrVal] = val;
9551                 }
9552             //See if we wanted to overload this property
9553             std::map<String, String>::iterator iter =
9554                 specifiedProperties.find(attrVal);
9555             if (iter != specifiedProperties.end())
9556                 {
9557                 val = iter->second;
9558                 status("overloading property '%s' = '%s'",
9559                     attrVal.c_str(), val.c_str());
9560                 properties[attrVal] = val;
9561                 }
9562             }
9563         else if (attrName == "file")
9564             {
9565             String prefix;
9566             if (!getAttribute(elem, "prefix", prefix))
9567                 return false;
9568             if (prefix.size() > 0)
9569                 {
9570                 if (prefix[prefix.size()-1] != '.')
9571                     prefix.push_back('.');
9572                 }
9573             if (!parsePropertyFile(attrName, prefix))
9574                 return false;
9575             }
9576         else if (attrName == "environment")
9577             {
9578             if (attrVal.find('.') != attrVal.npos)
9579                 {
9580                 error("environment prefix cannot have a '.' in it");
9581                 return false;
9582                 }
9583             envPrefix = attrVal;
9584             envPrefix.push_back('.');
9585             }
9586         else if (attrName == "pkg-config")
9587             {
9588             if (attrVal.find('.') != attrVal.npos)
9589                 {
9590                 error("pkg-config prefix cannot have a '.' in it");
9591                 return false;
9592                 }
9593             pcPrefix = attrVal;
9594             pcPrefix.push_back('.');
9595             }
9596         else if (attrName == "pkg-config-cflags")
9597             {
9598             if (attrVal.find('.') != attrVal.npos)
9599                 {
9600                 error("pkg-config-cflags prefix cannot have a '.' in it");
9601                 return false;
9602                 }
9603             pccPrefix = attrVal;
9604             pccPrefix.push_back('.');
9605             }
9606         else if (attrName == "pkg-config-libs")
9607             {
9608             if (attrVal.find('.') != attrVal.npos)
9609                 {
9610                 error("pkg-config-libs prefix cannot have a '.' in it");
9611                 return false;
9612                 }
9613             pclPrefix = attrVal;
9614             pclPrefix.push_back('.');
9615             }
9616         }
9618     return true;
9624 /**
9625  *
9626  */
9627 bool Make::parseFile()
9629     status("######## PARSE : %s", uri.getPath().c_str());
9631     setLine(0);
9633     Parser parser;
9634     Element *root = parser.parseFile(uri.getNativePath());
9635     if (!root)
9636         {
9637         error("Could not open %s for reading",
9638               uri.getNativePath().c_str());
9639         return false;
9640         }
9641     
9642     setLine(root->getLine());
9644     if (root->getChildren().size()==0 ||
9645         root->getChildren()[0]->getName()!="project")
9646         {
9647         error("Main xml element should be <project>");
9648         delete root;
9649         return false;
9650         }
9652     //########## Project attributes
9653     Element *project = root->getChildren()[0];
9654     String s = project->getAttribute("name");
9655     if (s.size() > 0)
9656         projectName = s;
9657     s = project->getAttribute("default");
9658     if (s.size() > 0)
9659         defaultTarget = s;
9660     s = project->getAttribute("basedir");
9661     if (s.size() > 0)
9662         baseDir = s;
9664     //######### PARSE MEMBERS
9665     std::vector<Element *> children = project->getChildren();
9666     for (unsigned int i=0 ; i<children.size() ; i++)
9667         {
9668         Element *elem = children[i];
9669         setLine(elem->getLine());
9670         String tagName = elem->getName();
9672         //########## DESCRIPTION
9673         if (tagName == "description")
9674             {
9675             description = parser.trim(elem->getValue());
9676             }
9678         //######### PROPERTY
9679         else if (tagName == "property")
9680             {
9681             if (!parseProperty(elem))
9682                 return false;
9683             }
9685         //######### TARGET
9686         else if (tagName == "target")
9687             {
9688             String tname   = elem->getAttribute("name");
9689             String tdesc   = elem->getAttribute("description");
9690             String tdeps   = elem->getAttribute("depends");
9691             String tif     = elem->getAttribute("if");
9692             String tunless = elem->getAttribute("unless");
9693             Target target(*this);
9694             target.setName(tname);
9695             target.setDescription(tdesc);
9696             target.parseDependencies(tdeps);
9697             target.setIf(tif);
9698             target.setUnless(tunless);
9699             std::vector<Element *> telems = elem->getChildren();
9700             for (unsigned int i=0 ; i<telems.size() ; i++)
9701                 {
9702                 Element *telem = telems[i];
9703                 Task breeder(*this);
9704                 Task *task = breeder.createTask(telem, telem->getLine());
9705                 if (!task)
9706                     return false;
9707                 allTasks.push_back(task);
9708                 target.addTask(task);
9709                 }
9711             //Check name
9712             if (tname.size() == 0)
9713                 {
9714                 error("no name for target");
9715                 return false;
9716                 }
9717             //Check for duplicate name
9718             if (targets.find(tname) != targets.end())
9719                 {
9720                 error("target '%s' already defined", tname.c_str());
9721                 return false;
9722                 }
9723             //more work than targets[tname]=target, but avoids default allocator
9724             targets.insert(std::make_pair<String, Target>(tname, target));
9725             }
9726         //######### none of the above
9727         else
9728             {
9729             error("unknown toplevel tag: <%s>", tagName.c_str());
9730             return false;
9731             }
9733         }
9735     std::map<String, Target>::iterator iter;
9736     for (iter = targets.begin() ; iter!= targets.end() ; iter++)
9737         {
9738         Target tgt = iter->second;
9739         std::vector<String> depList;
9740         if (!checkTargetDependencies(tgt, depList))
9741             {
9742             return false;
9743             }
9744         }
9747     delete root;
9748     status("######## PARSE COMPLETE");
9749     return true;
9753 /**
9754  * Overload a <property>
9755  */
9756 bool Make::specifyProperty(const String &name, const String &value)
9758     if (specifiedProperties.find(name) != specifiedProperties.end())
9759         {
9760         error("Property %s already specified", name.c_str());
9761         return false;
9762         }
9763     specifiedProperties[name] = value;
9764     return true;
9769 /**
9770  *
9771  */
9772 bool Make::run()
9774     if (!parseFile())
9775         return false;
9776         
9777     if (!execute())
9778         return false;
9780     return true;
9786 /**
9787  * Get a formatted MM:SS.sss time elapsed string
9788  */ 
9789 static String
9790 timeDiffString(struct timeval &x, struct timeval &y)
9792     long microsX  = x.tv_usec;
9793     long secondsX = x.tv_sec;
9794     long microsY  = y.tv_usec;
9795     long secondsY = y.tv_sec;
9796     if (microsX < microsY)
9797         {
9798         microsX += 1000000;
9799         secondsX -= 1;
9800         }
9802     int seconds = (int)(secondsX - secondsY);
9803     int millis  = (int)((microsX - microsY)/1000);
9805     int minutes = seconds/60;
9806     seconds -= minutes*60;
9807     char buf[80];
9808     snprintf(buf, 79, "%dm %d.%03ds", minutes, seconds, millis);
9809     String ret = buf;
9810     return ret;
9811     
9814 /**
9815  *
9816  */
9817 bool Make::run(const String &target)
9819     status("####################################################");
9820     status("#   %s", version().c_str());
9821     status("####################################################");
9822     struct timeval timeStart, timeEnd;
9823     ::gettimeofday(&timeStart, NULL);
9824     specifiedTarget = target;
9825     if (!run())
9826         return false;
9827     ::gettimeofday(&timeEnd, NULL);
9828     String timeStr = timeDiffString(timeEnd, timeStart);
9829     status("####################################################");
9830     status("#   BuildTool Completed : %s", timeStr.c_str());
9831     status("####################################################");
9832     return true;
9841 }// namespace buildtool
9842 //########################################################################
9843 //# M A I N
9844 //########################################################################
9846 typedef buildtool::String String;
9848 /**
9849  *  Format an error message in printf() style
9850  */
9851 static void error(const char *fmt, ...)
9853     va_list ap;
9854     va_start(ap, fmt);
9855     fprintf(stderr, "BuildTool error: ");
9856     vfprintf(stderr, fmt, ap);
9857     fprintf(stderr, "\n");
9858     va_end(ap);
9862 static bool parseProperty(const String &s, String &name, String &val)
9864     int len = s.size();
9865     int i;
9866     for (i=0 ; i<len ; i++)
9867         {
9868         char ch = s[i];
9869         if (ch == '=')
9870             break;
9871         name.push_back(ch);
9872         }
9873     if (i>=len || s[i]!='=')
9874         {
9875         error("property requires -Dname=value");
9876         return false;
9877         }
9878     i++;
9879     for ( ; i<len ; i++)
9880         {
9881         char ch = s[i];
9882         val.push_back(ch);
9883         }
9884     return true;
9888 /**
9889  * Compare a buffer with a key, for the length of the key
9890  */
9891 static bool sequ(const String &buf, const char *key)
9893     int len = buf.size();
9894     for (int i=0 ; key[i] && i<len ; i++)
9895         {
9896         if (key[i] != buf[i])
9897             return false;
9898         }        
9899     return true;
9902 static void usage(int argc, char **argv)
9904     printf("usage:\n");
9905     printf("   %s [options] [target]\n", argv[0]);
9906     printf("Options:\n");
9907     printf("  -help, -h              print this message\n");
9908     printf("  -version               print the version information and exit\n");
9909     printf("  -file <file>           use given buildfile\n");
9910     printf("  -f <file>                 ''\n");
9911     printf("  -D<property>=<value>   use value for given property\n");
9917 /**
9918  * Parse the command-line args, get our options,
9919  * and run this thing
9920  */   
9921 static bool parseOptions(int argc, char **argv)
9923     if (argc < 1)
9924         {
9925         error("Cannot parse arguments");
9926         return false;
9927         }
9929     buildtool::Make make;
9931     String target;
9933     //char *progName = argv[0];
9934     for (int i=1 ; i<argc ; i++)
9935         {
9936         String arg = argv[i];
9937         if (arg.size()>1 && arg[0]=='-')
9938             {
9939             if (arg == "-h" || arg == "-help")
9940                 {
9941                 usage(argc,argv);
9942                 return true;
9943                 }
9944             else if (arg == "-version")
9945                 {
9946                 printf("%s", make.version().c_str());
9947                 return true;
9948                 }
9949             else if (arg == "-f" || arg == "-file")
9950                 {
9951                 if (i>=argc)
9952                    {
9953                    usage(argc, argv);
9954                    return false;
9955                    }
9956                 i++; //eat option
9957                 make.setURI(argv[i]);
9958                 }
9959             else if (arg.size()>2 && sequ(arg, "-D"))
9960                 {
9961                 String s = arg.substr(2, arg.size());
9962                 String name, value;
9963                 if (!parseProperty(s, name, value))
9964                    {
9965                    usage(argc, argv);
9966                    return false;
9967                    }
9968                 if (!make.specifyProperty(name, value))
9969                     return false;
9970                 }
9971             else
9972                 {
9973                 error("Unknown option:%s", arg.c_str());
9974                 return false;
9975                 }
9976             }
9977         else
9978             {
9979             if (target.size()>0)
9980                 {
9981                 error("only one initial target");
9982                 usage(argc, argv);
9983                 return false;
9984                 }
9985             target = arg;
9986             }
9987         }
9989     //We have the options.  Now execute them
9990     if (!make.run(target))
9991         return false;
9993     return true;
9999 /*
10000 static bool runMake()
10002     buildtool::Make make;
10003     if (!make.run())
10004         return false;
10005     return true;
10009 static bool pkgConfigTest()
10011     buildtool::PkgConfig pkgConfig;
10012     if (!pkgConfig.readFile("gtk+-2.0.pc"))
10013         return false;
10014     return true;
10019 static bool depTest()
10021     buildtool::DepTool deptool;
10022     deptool.setSourceDirectory("/dev/ink/inkscape/src");
10023     if (!deptool.generateDependencies("build.dep"))
10024         return false;
10025     std::vector<buildtool::FileRec> res =
10026            deptool.loadDepFile("build.dep");
10027     if (res.size() == 0)
10028         return false;
10029     return true;
10032 static bool popenTest()
10034     buildtool::Make make;
10035     buildtool::String out, err;
10036     bool ret = make.executeCommand("gcc xx.cpp", "", out, err);
10037     printf("Popen test:%d '%s' '%s'\n", ret, out.c_str(), err.c_str());
10038     return true;
10042 static bool propFileTest()
10044     buildtool::Make make;
10045     make.parsePropertyFile("test.prop", "test.");
10046     return true;
10050 int main(int argc, char **argv)
10053     if (!parseOptions(argc, argv))
10054         return 1;
10055     /*
10056     if (!popenTest())
10057         return 1;
10059     if (!depTest())
10060         return 1;
10061     if (!propFileTest())
10062         return 1;
10063     if (runMake())
10064         return 1;
10065     */
10066     return 0;
10070 //########################################################################
10071 //# E N D 
10072 //########################################################################