Code

Translation 98 percent. German translation update.
[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.9"
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 //# Dir cache to speed up dir requests
2770 //########################################################################
2771 /*struct DirListing {
2772     bool available;
2773     std::vector<String> files;
2774     std::vector<String> dirs;
2775 };
2776 typedef std::map<String, DirListing > dirCacheType;
2777 static dirCacheType dirCache;
2778 static const DirListing &cachedDir(String fullDir)
2780     String dirNative = getNativePath(fullDir);
2781     std::pair<dirCacheType::iterator,bool> result = dirCache.insert(dirCacheType::value_type(dirNative, DirListing()));
2782     if (result.second) {
2783         DIR *dir = opendir(dirNative.c_str());
2784         if (!dir)
2785             {
2786             error("Could not open directory %s : %s",
2787                 dirNative.c_str(), strerror(errno));
2788             result.first->second.available = false;
2789             }
2790         else
2791             {
2792             result.first->second.available = true;
2793             while (true)
2794                 {
2795                 struct dirent *de = readdir(dir);
2796                 if (!de)
2797                     break;
2799                 //Get the directory member name
2800                 String s = de->d_name;
2801                 if (s.size() == 0 || s[0] == '.')
2802                     continue;
2803                 String childName;
2804                 if (dirName.size()>0)
2805                     {
2806                     childName.append(dirName);
2807                     childName.append("/");
2808                     }
2809                 childName.append(s);
2810                 String fullChild = baseDir;
2811                 fullChild.append("/");
2812                 fullChild.append(childName);
2813                 
2814                 if (isDirectory(fullChild))
2815                     {
2816                     //trace("directory: %s", childName.c_str());
2817                     if (!listFiles(baseDir, childName, res))
2818                         return false;
2819                     continue;
2820                     }
2821                 else if (!isRegularFile(fullChild))
2822                     {
2823                     error("unknown file:%s", childName.c_str());
2824                     return false;
2825                     }
2827             //all done!
2828                 res.push_back(childName);
2830                 }
2831             closedir(dir);
2832             }
2833     }
2834     return result.first->second;
2835 }*/
2837 //########################################################################
2838 //# F I L E S E T
2839 //########################################################################
2840 /**
2841  * This is the descriptor for a <fileset> item
2842  */
2843 class FileSet
2845 public:
2847     /**
2848      *
2849      */
2850     FileSet()
2851         {}
2853     /**
2854      *
2855      */
2856     FileSet(const FileSet &other)
2857         { assign(other); }
2859     /**
2860      *
2861      */
2862     FileSet &operator=(const FileSet &other)
2863         { assign(other); return *this; }
2865     /**
2866      *
2867      */
2868     virtual ~FileSet()
2869         {}
2871     /**
2872      *
2873      */
2874     String getDirectory() const
2875         { return directory; }
2876         
2877     /**
2878      *
2879      */
2880     void setDirectory(const String &val)
2881         { directory = val; }
2883     /**
2884      *
2885      */
2886     void setFiles(const std::vector<String> &val)
2887         { files = val; }
2889     /**
2890      *
2891      */
2892     std::vector<String> getFiles() const
2893         { return files; }
2894         
2895     /**
2896      *
2897      */
2898     void setIncludes(const std::vector<String> &val)
2899         { includes = val; }
2901     /**
2902      *
2903      */
2904     std::vector<String> getIncludes() const
2905         { return includes; }
2906         
2907     /**
2908      *
2909      */
2910     void setExcludes(const std::vector<String> &val)
2911         { excludes = val; }
2913     /**
2914      *
2915      */
2916     std::vector<String> getExcludes() const
2917         { return excludes; }
2918         
2919     /**
2920      *
2921      */
2922     unsigned int size() const
2923         { return files.size(); }
2924         
2925     /**
2926      *
2927      */
2928     String operator[](int index) const
2929         { return files[index]; }
2930         
2931     /**
2932      *
2933      */
2934     void clear()
2935         {
2936         directory = "";
2937         files.clear();
2938         includes.clear();
2939         excludes.clear();
2940         }
2941         
2943 private:
2945     void assign(const FileSet &other)
2946         {
2947         directory = other.directory;
2948         files     = other.files;
2949         includes  = other.includes;
2950         excludes  = other.excludes;
2951         }
2953     String directory;
2954     std::vector<String> files;
2955     std::vector<String> includes;
2956     std::vector<String> excludes;
2957 };
2960 //########################################################################
2961 //# F I L E L I S T
2962 //########################################################################
2963 /**
2964  * This is a simpler, explicitly-named list of files
2965  */
2966 class FileList
2968 public:
2970     /**
2971      *
2972      */
2973     FileList()
2974         {}
2976     /**
2977      *
2978      */
2979     FileList(const FileList &other)
2980         { assign(other); }
2982     /**
2983      *
2984      */
2985     FileList &operator=(const FileList &other)
2986         { assign(other); return *this; }
2988     /**
2989      *
2990      */
2991     virtual ~FileList()
2992         {}
2994     /**
2995      *
2996      */
2997     String getDirectory()
2998         { return directory; }
2999         
3000     /**
3001      *
3002      */
3003     void setDirectory(const String &val)
3004         { directory = val; }
3006     /**
3007      *
3008      */
3009     void setFiles(const std::vector<String> &val)
3010         { files = val; }
3012     /**
3013      *
3014      */
3015     std::vector<String> getFiles()
3016         { return files; }
3017         
3018     /**
3019      *
3020      */
3021     unsigned int size()
3022         { return files.size(); }
3023         
3024     /**
3025      *
3026      */
3027     String operator[](int index)
3028         { return files[index]; }
3029         
3030     /**
3031      *
3032      */
3033     void clear()
3034         {
3035         directory = "";
3036         files.clear();
3037         }
3038         
3040 private:
3042     void assign(const FileList &other)
3043         {
3044         directory = other.directory;
3045         files     = other.files;
3046         }
3048     String directory;
3049     std::vector<String> files;
3050 };
3055 //########################################################################
3056 //# M A K E    B A S E
3057 //########################################################################
3058 /**
3059  * Base class for all classes in this file
3060  */
3061 class MakeBase
3063 public:
3065     MakeBase()
3066         { line = 0; }
3067     virtual ~MakeBase()
3068         {}
3070     /**
3071      *     Return the URI of the file associated with this object 
3072      */     
3073     URI getURI()
3074         { return uri; }
3076     /**
3077      * Set the uri to the given string
3078      */
3079     void setURI(const String &uristr)
3080         { uri.parse(uristr); }
3082     /**
3083      *  Resolve another path relative to this one
3084      */
3085     String resolve(const String &otherPath);
3087     /**
3088      * replace variable refs like ${a} with their values
3089      * Assume that the string has already been syntax validated
3090      */
3091     String eval(const String &s, const String &defaultVal);
3093     /**
3094      * replace variable refs like ${a} with their values
3095      * return true or false
3096      * Assume that the string has already been syntax validated
3097      */
3098     bool evalBool(const String &s, bool defaultVal);
3100     /**
3101      *  Get an element attribute, performing substitutions if necessary
3102      */
3103     bool getAttribute(Element *elem, const String &name, String &result);
3105     /**
3106      * Get an element value, performing substitutions if necessary
3107      */
3108     bool getValue(Element *elem, String &result);
3109     
3110     /**
3111      * Set the current line number in the file
3112      */         
3113     void setLine(int val)
3114         { line = val; }
3115         
3116     /**
3117      * Get the current line number in the file
3118      */         
3119     int getLine()
3120         { return line; }
3123     /**
3124      * Set a property to a given value
3125      */
3126     virtual void setProperty(const String &name, const String &val)
3127         {
3128         properties[name] = val;
3129         }
3131     /**
3132      * Return a named property is found, else a null string
3133      */
3134     virtual String getProperty(const String &name)
3135         {
3136         String val;
3137         std::map<String, String>::iterator iter = properties.find(name);
3138         if (iter != properties.end())
3139             val = iter->second;
3140         String sval;
3141         if (!getSubstitutions(val, sval))
3142             return false;
3143         return sval;
3144         }
3146     /**
3147      * Return true if a named property is found, else false
3148      */
3149     virtual bool hasProperty(const String &name)
3150         {
3151         std::map<String, String>::iterator iter = properties.find(name);
3152         if (iter == properties.end())
3153             return false;
3154         return true;
3155         }
3158 protected:
3160     /**
3161      *    The path to the file associated with this object
3162      */     
3163     URI uri;
3164     
3165     /**
3166      *    If this prefix is seen in a substitution, use an environment
3167      *    variable.
3168      *             example:  <property environment="env"/>
3169      *             ${env.JAVA_HOME}
3170      */
3171     String envPrefix;
3173     /**
3174      *    If this prefix is seen in a substitution, use as a
3175      *    pkg-config 'all' query
3176      *             example:  <property pkg-config="pc"/>
3177      *             ${pc.gtkmm}
3178      */
3179     String pcPrefix;
3181     /**
3182      *    If this prefix is seen in a substitution, use as a
3183      *    pkg-config 'cflags' query
3184      *             example:  <property pkg-config="pcc"/>
3185      *             ${pcc.gtkmm}
3186      */
3187     String pccPrefix;
3189     /**
3190      *    If this prefix is seen in a substitution, use as a
3191      *    pkg-config 'libs' query
3192      *             example:  <property pkg-config-libs="pcl"/>
3193      *             ${pcl.gtkmm}
3194      */
3195     String pclPrefix;
3197     /**
3198      *    If this prefix is seen in a substitution, use as a
3199      *    Subversion "svn info" query
3200      *             example:  <property subversion="svn"/>
3201      *             ${svn.Revision}
3202      */
3203     String svnPrefix;
3209     /**
3210      *  Print a printf()-like formatted error message
3211      */
3212     void error(const char *fmt, ...);
3214     /**
3215      *  Print a printf()-like formatted trace message
3216      */
3217     void status(const char *fmt, ...);
3219     /**
3220      *  Show target status
3221      */
3222     void targetstatus(const char *fmt, ...);
3224     /**
3225      *  Print a printf()-like formatted trace message
3226      */
3227     void trace(const char *fmt, ...);
3229     /**
3230      *  Check if a given string matches a given regex pattern
3231      */
3232     bool regexMatch(const String &str, const String &pattern);
3234     /**
3235      *
3236      */
3237     String getSuffix(const String &fname);
3239     /**
3240      * Break up a string into substrings delimited the characters
3241      * in delimiters.  Null-length substrings are ignored
3242      */  
3243     std::vector<String> tokenize(const String &val,
3244                           const String &delimiters);
3246     /**
3247      *  replace runs of whitespace with a space
3248      */
3249     String strip(const String &s);
3251     /**
3252      *  remove leading whitespace from each line
3253      */
3254     String leftJustify(const String &s);
3256     /**
3257      *  remove leading and trailing whitespace from string
3258      */
3259     String trim(const String &s);
3261     /**
3262      *  Return a lower case version of the given string
3263      */
3264     String toLower(const String &s);
3266     /**
3267      * Return the native format of the canonical
3268      * path which we store
3269      */
3270     String getNativePath(const String &path);
3272     /**
3273      * Execute a shell command.  Outbuf is a ref to a string
3274      * to catch the result.     
3275      */         
3276     bool executeCommand(const String &call,
3277                         const String &inbuf,
3278                         String &outbuf,
3279                         String &errbuf);
3280     /**
3281      * List all directories in a given base and starting directory
3282      * It is usually called like:
3283      *        bool ret = listDirectories("src", "", result);    
3284      */         
3285     bool listDirectories(const String &baseName,
3286                          const String &dirname,
3287                          std::vector<String> &res);
3289     /**
3290      * Find all files in the named directory 
3291      */         
3292     bool listFiles(const String &baseName,
3293                    const String &dirname,
3294                    std::vector<String> &result);
3296     /**
3297      * Perform a listing for a fileset 
3298      */         
3299     bool listFiles(MakeBase &propRef, FileSet &fileSet);
3301     /**
3302      * Parse a <patternset>
3303      */  
3304     bool parsePatternSet(Element *elem,
3305                        MakeBase &propRef,
3306                        std::vector<String> &includes,
3307                        std::vector<String> &excludes);
3309     /**
3310      * Parse a <fileset> entry, and determine which files
3311      * should be included
3312      */  
3313     bool parseFileSet(Element *elem,
3314                     MakeBase &propRef,
3315                     FileSet &fileSet);
3316     /**
3317      * Parse a <filelist> entry
3318      */  
3319     bool parseFileList(Element *elem,
3320                     MakeBase &propRef,
3321                     FileList &fileList);
3323     /**
3324      * Return this object's property list
3325      */
3326     virtual std::map<String, String> &getProperties()
3327         { return properties; }
3330     std::map<String, String> properties;
3332     /**
3333      * Create a directory, making intermediate dirs
3334      * if necessary
3335      */                  
3336     bool createDirectory(const String &dirname);
3338     /**
3339      * Delete a directory and its children if desired
3340      */
3341     bool removeDirectory(const String &dirName);
3343     /**
3344      * Copy a file from one name to another. Perform only if needed
3345      */ 
3346     bool copyFile(const String &srcFile, const String &destFile);
3348     /**
3349      * Delete a file
3350      */ 
3351     bool removeFile(const String &file);
3353     /**
3354      * Tests if the file exists
3355      */ 
3356     bool fileExists(const String &fileName);
3358     /**
3359      * Tests if the file exists and is a regular file
3360      */ 
3361     bool isRegularFile(const String &fileName);
3363     /**
3364      * Tests if the file exists and is a directory
3365      */ 
3366     bool isDirectory(const String &fileName);
3368     /**
3369      * Tests is the modification date of fileA is newer than fileB
3370      */ 
3371     bool isNewerThan(const String &fileA, const String &fileB);
3373 private:
3375     bool pkgConfigRecursive(const String packageName,
3376                             const String &path, 
3377                             const String &prefix, 
3378                             int query,
3379                             String &result,
3380                             std::set<String> &deplist);
3382     /**
3383      * utility method to query for "all", "cflags", or "libs" for this package and its
3384      * dependencies.  0, 1, 2
3385      */          
3386     bool pkgConfigQuery(const String &packageName, int query, String &result);
3388     /**
3389      * replace a variable ref like ${a} with a value
3390      */
3391     bool lookupProperty(const String &s, String &result);
3392     
3393     /**
3394      * called by getSubstitutions().  This is in case a looked-up string
3395      * has substitutions also.     
3396      */
3397     bool getSubstitutionsRecursive(const String &s, String &result, int depth);
3399     /**
3400      * replace variable refs in a string like ${a} with their values
3401      */
3402     bool getSubstitutions(const String &s, String &result);
3404     int line;
3407 };
3411 /**
3412  * Define the pkg-config class here, since it will be used in MakeBase method
3413  * implementations. 
3414  */
3415 class PkgConfig : public MakeBase
3418 public:
3420     /**
3421      *
3422      */
3423     PkgConfig()
3424         {
3425          path   = ".";
3426          prefix = "/target";
3427          init();
3428          }
3430     /**
3431      *
3432      */
3433     PkgConfig(const PkgConfig &other)
3434         { assign(other); }
3436     /**
3437      *
3438      */
3439     PkgConfig &operator=(const PkgConfig &other)
3440         { assign(other); return *this; }
3442     /**
3443      *
3444      */
3445     virtual ~PkgConfig()
3446         { }
3448     /**
3449      *
3450      */
3451     virtual String getName()
3452         { return name; }
3454     /**
3455      *
3456      */
3457     virtual String getPath()
3458         { return path; }
3460     /**
3461      *
3462      */
3463     virtual void setPath(const String &val)
3464         { path = val; }
3466     /**
3467      *
3468      */
3469     virtual String getPrefix()
3470         { return prefix; }
3472     /**
3473      *  Allow the user to override the prefix in the file
3474      */
3475     virtual void setPrefix(const String &val)
3476         { prefix = val; }
3478     /**
3479      *
3480      */
3481     virtual String getDescription()
3482         { return description; }
3484     /**
3485      *
3486      */
3487     virtual String getCflags()
3488         { return cflags; }
3490     /**
3491      *
3492      */
3493     virtual String getLibs()
3494         { return libs; }
3496     /**
3497      *
3498      */
3499     virtual String getAll()
3500         {
3501          String ret = cflags;
3502          ret.append(" ");
3503          ret.append(libs);
3504          return ret;
3505         }
3507     /**
3508      *
3509      */
3510     virtual String getVersion()
3511         { return version; }
3513     /**
3514      *
3515      */
3516     virtual int getMajorVersion()
3517         { return majorVersion; }
3519     /**
3520      *
3521      */
3522     virtual int getMinorVersion()
3523         { return minorVersion; }
3525     /**
3526      *
3527      */
3528     virtual int getMicroVersion()
3529         { return microVersion; }
3531     /**
3532      *
3533      */
3534     virtual std::map<String, String> &getAttributes()
3535         { return attrs; }
3537     /**
3538      *
3539      */
3540     virtual std::vector<String> &getRequireList()
3541         { return requireList; }
3543     /**
3544      *  Read a file for its details
3545      */         
3546     virtual bool readFile(const String &fileName);
3548     /**
3549      *  Read a file for its details
3550      */         
3551     virtual bool query(const String &name);
3553 private:
3555     void init()
3556         {
3557         //do not set path and prefix here
3558         name         = "";
3559         description  = "";
3560         cflags       = "";
3561         libs         = "";
3562         requires     = "";
3563         version      = "";
3564         majorVersion = 0;
3565         minorVersion = 0;
3566         microVersion = 0;
3567         fileName     = "";
3568         attrs.clear();
3569         requireList.clear();
3570         }
3572     void assign(const PkgConfig &other)
3573         {
3574         name         = other.name;
3575         path         = other.path;
3576         prefix       = other.prefix;
3577         description  = other.description;
3578         cflags       = other.cflags;
3579         libs         = other.libs;
3580         requires     = other.requires;
3581         version      = other.version;
3582         majorVersion = other.majorVersion;
3583         minorVersion = other.minorVersion;
3584         microVersion = other.microVersion;
3585         fileName     = other.fileName;
3586         attrs        = other.attrs;
3587         requireList  = other.requireList;
3588         }
3592     int get(int pos);
3594     int skipwhite(int pos);
3596     int getword(int pos, String &ret);
3598     /**
3599      * Very important
3600      */         
3601     bool parseRequires();
3603     void parseVersion();
3605     bool parseLine(const String &lineBuf);
3607     bool parse(const String &buf);
3609     void dumpAttrs();
3611     String name;
3613     String path;
3615     String prefix;
3617     String description;
3619     String cflags;
3621     String libs;
3623     String requires;
3625     String version;
3627     int majorVersion;
3629     int minorVersion;
3631     int microVersion;
3633     String fileName;
3635     std::map<String, String> attrs;
3637     std::vector<String> requireList;
3639     char *parsebuf;
3640     int parselen;
3641 };
3645 /**
3646  * Execute the "svn info" command and parse the result.
3647  * This is a simple, small class. Define here, because it
3648  * is used by MakeBase implementation methods. 
3649  */
3650 class SvnInfo : public MakeBase
3652 public:
3654 #if 0
3655     /**
3656      * Safe way. Execute "svn info --xml" and parse the result.  Search for
3657      * elements/attributes.  Safe from changes in format.
3658      */
3659     bool query(const String &name, String &res)
3660     {
3661         String cmd = "svn info --xml";
3662     
3663         String outString, errString;
3664         bool ret = executeCommand(cmd.c_str(), "", outString, errString);
3665         if (!ret)
3666             {
3667             error("error executing '%s': %s", cmd.c_str(), errString.c_str());
3668             return false;
3669             }
3670         Parser parser;
3671         Element *elem = parser.parse(outString); 
3672         if (!elem)
3673             {
3674             error("error parsing 'svn info' xml result: %s", outString.c_str());
3675             return false;
3676             }
3677         
3678         res = elem->getTagValue(name);
3679         if (res.size()==0)
3680             {
3681             res = elem->getTagAttribute("entry", name);
3682             }
3683         return true;
3684     } 
3685 #else
3688     /**
3689      * Universal way.  Parse the file directly.  Not so safe from
3690      * changes in format.
3691      */
3692     bool query(const String &name, String &res)
3693     {
3694         String fileName = resolve(".svn/entries");
3695         String nFileName = getNativePath(fileName);
3696         
3697         std::map<String, String> properties;
3698         
3699         FILE *f = fopen(nFileName.c_str(), "r");
3700         if (!f)
3701             {
3702             error("could not open SVN 'entries' file");
3703             return false;
3704             }
3706         const char *fieldNames[] =
3707             {
3708             "format-nbr",
3709             "name",
3710             "kind",
3711             "revision",
3712             "url",
3713             "repos",
3714             "schedule",
3715             "text-time",
3716             "checksum",
3717             "committed-date",
3718             "committed-rev",
3719             "last-author",
3720             "has-props",
3721             "has-prop-mods",
3722             "cachable-props",
3723             };
3725         for (int i=0 ; i<15 ; i++)
3726             {
3727             inbuf[0] = '\0';
3728             if (feof(f) || !fgets(inbuf, 255, f))
3729                 break;
3730             properties[fieldNames[i]] = trim(inbuf);
3731             }
3732         fclose(f);
3733         
3734         res = properties[name];
3735         
3736         return true;
3737     } 
3738     
3739 private:
3741     char inbuf[256];
3743 #endif
3745 };
3752 /**
3753  *  Print a printf()-like formatted error message
3754  */
3755 void MakeBase::error(const char *fmt, ...)
3757     va_list args;
3758     va_start(args,fmt);
3759     fprintf(stderr, "Make error line %d: ", line);
3760     vfprintf(stderr, fmt, args);
3761     fprintf(stderr, "\n");
3762     va_end(args) ;
3767 /**
3768  *  Print a printf()-like formatted trace message
3769  */
3770 void MakeBase::status(const char *fmt, ...)
3772     va_list args;
3773     //fprintf(stdout, " ");
3774     va_start(args,fmt);
3775     vfprintf(stdout, fmt, args);
3776     va_end(args);
3777     fprintf(stdout, "\n");
3778     fflush(stdout);
3782 /**
3783  *  Print a printf()-like formatted trace message
3784  */
3785 void MakeBase::trace(const char *fmt, ...)
3787     va_list args;
3788     fprintf(stdout, "Make: ");
3789     va_start(args,fmt);
3790     vfprintf(stdout, fmt, args);
3791     va_end(args) ;
3792     fprintf(stdout, "\n");
3793     fflush(stdout);
3798 /**
3799  *  Resolve another path relative to this one
3800  */
3801 String MakeBase::resolve(const String &otherPath)
3803     URI otherURI(otherPath);
3804     URI fullURI = uri.resolve(otherURI);
3805     String ret = fullURI.toString();
3806     return ret;
3811 /**
3812  *  Check if a given string matches a given regex pattern
3813  */
3814 bool MakeBase::regexMatch(const String &str, const String &pattern)
3816     const TRexChar *terror = NULL;
3817     const TRexChar *cpat = pattern.c_str();
3818     TRex *expr = trex_compile(cpat, &terror);
3819     if (!expr)
3820         {
3821         if (!terror)
3822             terror = "undefined";
3823         error("compilation error [%s]!\n", terror);
3824         return false;
3825         } 
3827     bool ret = true;
3829     const TRexChar *cstr = str.c_str();
3830     if (trex_match(expr, cstr))
3831         {
3832         ret = true;
3833         }
3834     else
3835         {
3836         ret = false;
3837         }
3839     trex_free(expr);
3841     return ret;
3844 /**
3845  *  Return the suffix, if any, of a file name
3846  */
3847 String MakeBase::getSuffix(const String &fname)
3849     if (fname.size() < 2)
3850         return "";
3851     unsigned int pos = fname.find_last_of('.');
3852     if (pos == fname.npos)
3853         return "";
3854     pos++;
3855     String res = fname.substr(pos, fname.size()-pos);
3856     //trace("suffix:%s", res.c_str()); 
3857     return res;
3862 /**
3863  * Break up a string into substrings delimited the characters
3864  * in delimiters.  Null-length substrings are ignored
3865  */  
3866 std::vector<String> MakeBase::tokenize(const String &str,
3867                                 const String &delimiters)
3870     std::vector<String> res;
3871     char *del = (char *)delimiters.c_str();
3872     String dmp;
3873     for (unsigned int i=0 ; i<str.size() ; i++)
3874         {
3875         char ch = str[i];
3876         char *p = (char *)0;
3877         for (p=del ; *p ; p++)
3878             if (*p == ch)
3879                 break;
3880         if (*p)
3881             {
3882             if (dmp.size() > 0)
3883                 {
3884                 res.push_back(dmp);
3885                 dmp.clear();
3886                 }
3887             }
3888         else
3889             {
3890             dmp.push_back(ch);
3891             }
3892         }
3893     //Add tail
3894     if (dmp.size() > 0)
3895         {
3896         res.push_back(dmp);
3897         dmp.clear();
3898         }
3900     return res;
3905 /**
3906  *  replace runs of whitespace with a single space
3907  */
3908 String MakeBase::strip(const String &s)
3910     int len = s.size();
3911     String stripped;
3912     for (int i = 0 ; i<len ; i++)
3913         {
3914         char ch = s[i];
3915         if (isspace(ch))
3916             {
3917             stripped.push_back(' ');
3918             for ( ; i<len ; i++)
3919                 {
3920                 ch = s[i];
3921                 if (!isspace(ch))
3922                     {
3923                     stripped.push_back(ch);
3924                     break;
3925                     }
3926                 }
3927             }
3928         else
3929             {
3930             stripped.push_back(ch);
3931             }
3932         }
3933     return stripped;
3936 /**
3937  *  remove leading whitespace from each line
3938  */
3939 String MakeBase::leftJustify(const String &s)
3941     String out;
3942     int len = s.size();
3943     for (int i = 0 ; i<len ; )
3944         {
3945         char ch;
3946         //Skip to first visible character
3947         while (i<len)
3948             {
3949             ch = s[i];
3950             if (ch == '\n' || ch == '\r'
3951               || !isspace(ch))
3952                   break;
3953             i++;
3954             }
3955         //Copy the rest of the line
3956         while (i<len)
3957             {
3958             ch = s[i];
3959             if (ch == '\n' || ch == '\r')
3960                 {
3961                 if (ch != '\r')
3962                     out.push_back('\n');
3963                 i++;
3964                 break;
3965                 }
3966             else
3967                 {
3968                 out.push_back(ch);
3969                 }
3970             i++;
3971             }
3972         }
3973     return out;
3977 /**
3978  *  Removes whitespace from beginning and end of a string
3979  */
3980 String MakeBase::trim(const String &s)
3982     if (s.size() < 1)
3983         return s;
3984     
3985     //Find first non-ws char
3986     unsigned int begin = 0;
3987     for ( ; begin < s.size() ; begin++)
3988         {
3989         if (!isspace(s[begin]))
3990             break;
3991         }
3993     //Find first non-ws char, going in reverse
3994     unsigned int end = s.size() - 1;
3995     for ( ; end > begin ; end--)
3996         {
3997         if (!isspace(s[end]))
3998             break;
3999         }
4000     //trace("begin:%d  end:%d", begin, end);
4002     String res = s.substr(begin, end-begin+1);
4003     return res;
4007 /**
4008  *  Return a lower case version of the given string
4009  */
4010 String MakeBase::toLower(const String &s)
4012     if (s.size()==0)
4013         return s;
4015     String ret;
4016     for(unsigned int i=0; i<s.size() ; i++)
4017         {
4018         ret.push_back(tolower(s[i]));
4019         }
4020     return ret;
4024 /**
4025  * Return the native format of the canonical
4026  * path which we store
4027  */
4028 String MakeBase::getNativePath(const String &path)
4030 #ifdef __WIN32__
4031     String npath;
4032     unsigned int firstChar = 0;
4033     if (path.size() >= 3)
4034         {
4035         if (path[0] == '/' &&
4036             isalpha(path[1]) &&
4037             path[2] == ':')
4038             firstChar++;
4039         }
4040     for (unsigned int i=firstChar ; i<path.size() ; i++)
4041         {
4042         char ch = path[i];
4043         if (ch == '/')
4044             npath.push_back('\\');
4045         else
4046             npath.push_back(ch);
4047         }
4048     return npath;
4049 #else
4050     return path;
4051 #endif
4055 #ifdef __WIN32__
4056 #include <tchar.h>
4058 static String win32LastError()
4061     DWORD dw = GetLastError(); 
4063     LPVOID str;
4064     FormatMessage(
4065         FORMAT_MESSAGE_ALLOCATE_BUFFER | 
4066         FORMAT_MESSAGE_FROM_SYSTEM,
4067         NULL,
4068         dw,
4069         0,
4070         (LPTSTR) &str,
4071         0, NULL );
4072     LPTSTR p = _tcschr((const char *)str, _T('\r'));
4073     if(p != NULL)
4074         { // lose CRLF
4075         *p = _T('\0');
4076         }
4077     String ret = (char *)str;
4078     LocalFree(str);
4080     return ret;
4082 #endif
4087 #ifdef __WIN32__
4089 /**
4090  * Execute a system call, using pipes to send data to the
4091  * program's stdin,  and reading stdout and stderr.
4092  */
4093 bool MakeBase::executeCommand(const String &command,
4094                               const String &inbuf,
4095                               String &outbuf,
4096                               String &errbuf)
4099     status("============ cmd ============\n%s\n=============================",
4100                 command.c_str());
4102     outbuf.clear();
4103     errbuf.clear();
4104     
4106     /*
4107     I really hate having win32 code in this program, but the
4108     read buffer in command.com and cmd.exe are just too small
4109     for the large commands we need for compiling and linking.
4110     */
4112     bool ret = true;
4114     //# Allocate a separate buffer for safety
4115     char *paramBuf = new char[command.size() + 1];
4116     if (!paramBuf)
4117        {
4118        error("executeCommand cannot allocate command buffer");
4119        return false;
4120        }
4121     strcpy(paramBuf, (char *)command.c_str());
4123     //# Go to http://msdn2.microsoft.com/en-us/library/ms682499.aspx
4124     //# to see how Win32 pipes work
4126     //# Create pipes
4127     SECURITY_ATTRIBUTES saAttr; 
4128     saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); 
4129     saAttr.bInheritHandle = TRUE; 
4130     saAttr.lpSecurityDescriptor = NULL; 
4131     HANDLE stdinRead,  stdinWrite;
4132     HANDLE stdoutRead, stdoutWrite;
4133     HANDLE stderrRead, stderrWrite;
4134     if (!CreatePipe(&stdinRead, &stdinWrite, &saAttr, 0))
4135         {
4136         error("executeProgram: could not create pipe");
4137         delete[] paramBuf;
4138         return false;
4139         } 
4140     SetHandleInformation(stdinWrite, HANDLE_FLAG_INHERIT, 0);
4141     if (!CreatePipe(&stdoutRead, &stdoutWrite, &saAttr, 0))
4142         {
4143         error("executeProgram: could not create pipe");
4144         delete[] paramBuf;
4145         return false;
4146         } 
4147     SetHandleInformation(stdoutRead, HANDLE_FLAG_INHERIT, 0);
4148     if (&outbuf != &errbuf) {
4149         if (!CreatePipe(&stderrRead, &stderrWrite, &saAttr, 0))
4150             {
4151             error("executeProgram: could not create pipe");
4152             delete[] paramBuf;
4153             return false;
4154             } 
4155         SetHandleInformation(stderrRead, HANDLE_FLAG_INHERIT, 0);
4156     } else {
4157         stderrRead = stdoutRead;
4158         stderrWrite = stdoutWrite;
4159     }
4161     // Create the process
4162     STARTUPINFO siStartupInfo;
4163     PROCESS_INFORMATION piProcessInfo;
4164     memset(&siStartupInfo, 0, sizeof(siStartupInfo));
4165     memset(&piProcessInfo, 0, sizeof(piProcessInfo));
4166     siStartupInfo.cb = sizeof(siStartupInfo);
4167     siStartupInfo.hStdError   =  stderrWrite;
4168     siStartupInfo.hStdOutput  =  stdoutWrite;
4169     siStartupInfo.hStdInput   =  stdinRead;
4170     siStartupInfo.dwFlags    |=  STARTF_USESTDHANDLES;
4171    
4172     if (!CreateProcess(NULL, paramBuf, NULL, NULL, true,
4173                 0, NULL, NULL, &siStartupInfo,
4174                 &piProcessInfo))
4175         {
4176         error("executeCommand : could not create process : %s",
4177                     win32LastError().c_str());
4178         ret = false;
4179         }
4181     delete[] paramBuf;
4183     DWORD bytesWritten;
4184     if (inbuf.size()>0 &&
4185         !WriteFile(stdinWrite, inbuf.c_str(), inbuf.size(), 
4186                &bytesWritten, NULL))
4187         {
4188         error("executeCommand: could not write to pipe");
4189         return false;
4190         }    
4191     if (!CloseHandle(stdinWrite))
4192         {          
4193         error("executeCommand: could not close write pipe");
4194         return false;
4195         }
4196     if (!CloseHandle(stdoutWrite))
4197         {
4198         error("executeCommand: could not close read pipe");
4199         return false;
4200         }
4201     if (stdoutWrite != stderrWrite && !CloseHandle(stderrWrite))
4202         {
4203         error("executeCommand: could not close read pipe");
4204         return false;
4205         }
4207     bool lastLoop = false;
4208     while (true)
4209         {
4210         DWORD avail;
4211         DWORD bytesRead;
4212         char readBuf[4096];
4214         //trace("## stderr");
4215         PeekNamedPipe(stderrRead, NULL, 0, NULL, &avail, NULL);
4216         if (avail > 0)
4217             {
4218             bytesRead = 0;
4219             if (avail>4096) avail = 4096;
4220             ReadFile(stderrRead, readBuf, avail, &bytesRead, NULL);
4221             if (bytesRead > 0)
4222                 {
4223                 for (unsigned int i=0 ; i<bytesRead ; i++)
4224                     errbuf.push_back(readBuf[i]);
4225                 }
4226             }
4228         //trace("## stdout");
4229         PeekNamedPipe(stdoutRead, NULL, 0, NULL, &avail, NULL);
4230         if (avail > 0)
4231             {
4232             bytesRead = 0;
4233             if (avail>4096) avail = 4096;
4234             ReadFile(stdoutRead, readBuf, avail, &bytesRead, NULL);
4235             if (bytesRead > 0)
4236                 {
4237                 for (unsigned int i=0 ; i<bytesRead ; i++)
4238                     outbuf.push_back(readBuf[i]);
4239                 }
4240             }
4241             
4242         //Was this the final check after program done?
4243         if (lastLoop)
4244             break;
4246         DWORD exitCode;
4247         GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
4248         if (exitCode != STILL_ACTIVE)
4249             lastLoop = true;
4251         Sleep(10);
4252         }    
4253     //trace("outbuf:%s", outbuf.c_str());
4254     if (!CloseHandle(stdoutRead))
4255         {
4256         error("executeCommand: could not close read pipe");
4257         return false;
4258         }
4259     if (stdoutRead != stderrRead && !CloseHandle(stderrRead))
4260         {
4261         error("executeCommand: could not close read pipe");
4262         return false;
4263         }
4265     DWORD exitCode;
4266     GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
4267     //trace("exit code:%d", exitCode);
4268     if (exitCode != 0)
4269         {
4270         ret = false;
4271         }
4272     
4273     CloseHandle(piProcessInfo.hProcess);
4274     CloseHandle(piProcessInfo.hThread);
4276     return ret;
4278
4280 #else  /*do it unix style*/
4282 #include <sys/wait.h>
4286 /**
4287  * Execute a system call, using pipes to send data to the
4288  * program's stdin,  and reading stdout and stderr.
4289  */
4290 bool MakeBase::executeCommand(const String &command,
4291                               const String &inbuf,
4292                               String &outbuf,
4293                               String &errbuf)
4296     status("============ cmd ============\n%s\n=============================",
4297                 command.c_str());
4299     outbuf.clear();
4300     errbuf.clear();
4301     
4303     int outfds[2];
4304     if (pipe(outfds) < 0)
4305         return false;
4306     int errfds[2];
4307     if (pipe(errfds) < 0)
4308         return false;
4309     int pid = fork();
4310     if (pid < 0)
4311         {
4312         close(outfds[0]);
4313         close(outfds[1]);
4314         close(errfds[0]);
4315         close(errfds[1]);
4316         error("launch of command '%s' failed : %s",
4317              command.c_str(), strerror(errno));
4318         return false;
4319         }
4320     else if (pid > 0) // parent
4321         {
4322         close(outfds[1]);
4323         close(errfds[1]);
4324         }
4325     else // == 0, child
4326         {
4327         close(outfds[0]);
4328         dup2(outfds[1], STDOUT_FILENO);
4329         close(outfds[1]);
4330         close(errfds[0]);
4331         dup2(errfds[1], STDERR_FILENO);
4332         close(errfds[1]);
4334         char *args[4];
4335         args[0] = (char *)"sh";
4336         args[1] = (char *)"-c";
4337         args[2] = (char *)command.c_str();
4338         args[3] = NULL;
4339         execv("/bin/sh", args);
4340         exit(EXIT_FAILURE);
4341         }
4343     String outb;
4344     String errb;
4346     int outRead = outfds[0];
4347     int errRead = errfds[0];
4348     int max = outRead;
4349     if (errRead > max)
4350         max = errRead;
4352     bool outOpen = true;
4353     bool errOpen = true;
4355     while (outOpen || errOpen)
4356         {
4357         char ch;
4358         fd_set fdset;
4359         FD_ZERO(&fdset);
4360         if (outOpen)
4361             FD_SET(outRead, &fdset);
4362         if (errOpen)
4363             FD_SET(errRead, &fdset);
4364         int ret = select(max+1, &fdset, NULL, NULL, NULL);
4365         if (ret < 0)
4366             break;
4367         if (FD_ISSET(outRead, &fdset))
4368             {
4369             if (read(outRead, &ch, 1) <= 0)
4370                 { outOpen = false; }
4371             else if (ch <= 0)
4372                 { /* outOpen = false; */ }
4373             else
4374                 { outb.push_back(ch); }
4375             }
4376         if (FD_ISSET(errRead, &fdset))
4377             {
4378             if (read(errRead, &ch, 1) <= 0)
4379                 { errOpen = false; }
4380             else if (ch <= 0)
4381                 { /* errOpen = false; */ }
4382             else
4383                 { errb.push_back(ch); }
4384             }
4385         }
4387     int childReturnValue;
4388     wait(&childReturnValue);
4390     close(outRead);
4391     close(errRead);
4393     outbuf = outb;
4394     errbuf = errb;
4396     if (childReturnValue != 0)
4397         {
4398         error("exec of command '%s' failed : %s",
4399              command.c_str(), strerror(childReturnValue));
4400         return false;
4401         }
4403     return true;
4404
4406 #endif
4411 bool MakeBase::listDirectories(const String &baseName,
4412                               const String &dirName,
4413                               std::vector<String> &res)
4415     res.push_back(dirName);
4416     String fullPath = baseName;
4417     if (dirName.size()>0)
4418         {
4419         if (dirName[0]!='/') fullPath.append("/");
4420         fullPath.append(dirName);
4421         }
4422     DIR *dir = opendir(fullPath.c_str());
4423     while (true)
4424         {
4425         struct dirent *de = readdir(dir);
4426         if (!de)
4427             break;
4429         //Get the directory member name
4430         String s = de->d_name;
4431         if (s.size() == 0 || s[0] == '.')
4432             continue;
4433         String childName = dirName;
4434         childName.append("/");
4435         childName.append(s);
4437         String fullChildPath = baseName;
4438         fullChildPath.append("/");
4439         fullChildPath.append(childName);
4440         struct stat finfo;
4441         String childNative = getNativePath(fullChildPath);
4442         if (cachedStat(childNative, &finfo)<0)
4443             {
4444             error("cannot stat file:%s", childNative.c_str());
4445             }
4446         else if (S_ISDIR(finfo.st_mode))
4447             {
4448             //trace("directory: %s", childName.c_str());
4449             if (!listDirectories(baseName, childName, res))
4450                 return false;
4451             }
4452         }
4453     closedir(dir);
4455     return true;
4459 bool MakeBase::listFiles(const String &baseDir,
4460                          const String &dirName,
4461                          std::vector<String> &res)
4463     String fullDir = baseDir;
4464     if (dirName.size()>0)
4465         {
4466         fullDir.append("/");
4467         fullDir.append(dirName);
4468         }
4469     String dirNative = getNativePath(fullDir);
4471     std::vector<String> subdirs;
4472     DIR *dir = opendir(dirNative.c_str());
4473     if (!dir)
4474         {
4475         error("Could not open directory %s : %s",
4476               dirNative.c_str(), strerror(errno));
4477         return false;
4478         }
4479     while (true)
4480         {
4481         struct dirent *de = readdir(dir);
4482         if (!de)
4483             break;
4485         //Get the directory member name
4486         String s = de->d_name;
4487         if (s.size() == 0 || s[0] == '.')
4488             continue;
4489         String childName;
4490         if (dirName.size()>0)
4491             {
4492             childName.append(dirName);
4493             childName.append("/");
4494             }
4495         childName.append(s);
4496         String fullChild = baseDir;
4497         fullChild.append("/");
4498         fullChild.append(childName);
4499         
4500         if (isDirectory(fullChild))
4501             {
4502             //trace("directory: %s", childName.c_str());
4503             if (!listFiles(baseDir, childName, res))
4504                 return false;
4505             continue;
4506             }
4507         else if (!isRegularFile(fullChild))
4508             {
4509             error("unknown file:%s", childName.c_str());
4510             return false;
4511             }
4513        //all done!
4514         res.push_back(childName);
4516         }
4517     closedir(dir);
4519     return true;
4523 /**
4524  * Several different classes extend MakeBase.  By "propRef", we mean
4525  * the one holding the properties.  Likely "Make" itself
4526  */
4527 bool MakeBase::listFiles(MakeBase &propRef, FileSet &fileSet)
4529     //before doing the list,  resolve any property references
4530     //that might have been specified in the directory name, such as ${src}
4531     String fsDir = fileSet.getDirectory();
4532     String dir;
4533     if (!propRef.getSubstitutions(fsDir, dir))
4534         return false;
4535     String baseDir = propRef.resolve(dir);
4536     std::vector<String> fileList;
4537     if (!listFiles(baseDir, "", fileList))
4538         return false;
4540     std::vector<String> includes = fileSet.getIncludes();
4541     std::vector<String> excludes = fileSet.getExcludes();
4543     std::vector<String> incs;
4544     std::vector<String>::iterator iter;
4546     std::sort(fileList.begin(), fileList.end());
4548     //If there are <includes>, then add files to the output
4549     //in the order of the include list
4550     if (includes.size()==0)
4551         incs = fileList;
4552     else
4553         {
4554         for (iter = includes.begin() ; iter != includes.end() ; iter++)
4555             {
4556             String &pattern = *iter;
4557             std::vector<String>::iterator siter;
4558             for (siter = fileList.begin() ; siter != fileList.end() ; siter++)
4559                 {
4560                 String s = *siter;
4561                 if (regexMatch(s, pattern))
4562                     {
4563                     //trace("INCLUDED:%s", s.c_str());
4564                     incs.push_back(s);
4565                     }
4566                 }
4567             }
4568         }
4570     //Now trim off the <excludes>
4571     std::vector<String> res;
4572     for (iter = incs.begin() ; iter != incs.end() ; iter++)
4573         {
4574         String s = *iter;
4575         bool skipme = false;
4576         std::vector<String>::iterator siter;
4577         for (siter = excludes.begin() ; siter != excludes.end() ; siter++)
4578             {
4579             String &pattern = *siter;
4580             if (regexMatch(s, pattern))
4581                 {
4582                 //trace("EXCLUDED:%s", s.c_str());
4583                 skipme = true;
4584                 break;
4585                 }
4586             }
4587         if (!skipme)
4588             res.push_back(s);
4589         }
4590         
4591     fileSet.setFiles(res);
4593     return true;
4597 /**
4598  * 0 == all, 1 = cflags, 2 = libs
4599  */ 
4600 bool MakeBase::pkgConfigRecursive(const String packageName,
4601                                   const String &path, 
4602                                   const String &prefix, 
4603                                   int query,
4604                                   String &result,
4605                                   std::set<String> &deplist) 
4607     PkgConfig pkgConfig;
4608     if (path.size() > 0)
4609         pkgConfig.setPath(path);
4610     if (prefix.size() > 0)
4611         pkgConfig.setPrefix(prefix);
4612     if (!pkgConfig.query(packageName))
4613         return false;
4614     if (query == 0)
4615         result = pkgConfig.getAll();
4616     else if (query == 1)
4617         result = pkgConfig.getCflags();
4618     else
4619         result = pkgConfig.getLibs();
4620     deplist.insert(packageName);
4621     std::vector<String> list = pkgConfig.getRequireList();
4622     for (unsigned int i = 0 ; i<list.size() ; i++)
4623         {
4624         String depPkgName = list[i];
4625         if (deplist.find(depPkgName) != deplist.end())
4626             continue;
4627         String val;
4628         if (!pkgConfigRecursive(depPkgName, path, prefix, query, val, deplist))
4629             {
4630             error("Based on 'requires' attribute of package '%s'", packageName.c_str());
4631             return false;
4632             }
4633         result.append(" ");
4634         result.append(val);
4635         }
4637     return true;
4640 bool MakeBase::pkgConfigQuery(const String &packageName, int query, String &result)
4642     std::set<String> deplist;
4643     String path = getProperty("pkg-config-path");
4644     if (path.size()>0)
4645         path = resolve(path);
4646     String prefix = getProperty("pkg-config-prefix");
4647     String val;
4648     if (!pkgConfigRecursive(packageName, path, prefix, query, val, deplist))
4649         return false;
4650     result = val;
4651     return true;
4656 /**
4657  * replace a variable ref like ${a} with a value
4658  */
4659 bool MakeBase::lookupProperty(const String &propertyName, String &result)
4661     String varname = propertyName;
4662     if (envPrefix.size() > 0 &&
4663         varname.compare(0, envPrefix.size(), envPrefix) == 0)
4664         {
4665         varname = varname.substr(envPrefix.size());
4666         char *envstr = getenv(varname.c_str());
4667         if (!envstr)
4668             {
4669             error("environment variable '%s' not defined", varname.c_str());
4670             return false;
4671             }
4672         result = envstr;
4673         }
4674     else if (pcPrefix.size() > 0 &&
4675         varname.compare(0, pcPrefix.size(), pcPrefix) == 0)
4676         {
4677         varname = varname.substr(pcPrefix.size());
4678         String val;
4679         if (!pkgConfigQuery(varname, 0, val))
4680             return false;
4681         result = val;
4682         }
4683     else if (pccPrefix.size() > 0 &&
4684         varname.compare(0, pccPrefix.size(), pccPrefix) == 0)
4685         {
4686         varname = varname.substr(pccPrefix.size());
4687         String val;
4688         if (!pkgConfigQuery(varname, 1, val))
4689             return false;
4690         result = val;
4691         }
4692     else if (pclPrefix.size() > 0 &&
4693         varname.compare(0, pclPrefix.size(), pclPrefix) == 0)
4694         {
4695         varname = varname.substr(pclPrefix.size());
4696         String val;
4697         if (!pkgConfigQuery(varname, 2, val))
4698             return false;
4699         result = val;
4700         }
4701     else if (svnPrefix.size() > 0 &&
4702         varname.compare(0, svnPrefix.size(), svnPrefix) == 0)
4703         {
4704         varname = varname.substr(svnPrefix.size());
4705         String val;
4706         SvnInfo svnInfo;
4707         if (varname == "revision")
4708             {
4709             if (!svnInfo.query(varname, val))
4710                 return "";
4711             result = "r"+val;
4712             }
4713         if (!svnInfo.query(varname, val))
4714             return false;
4715         result = val;
4716         }
4717     else
4718         {
4719         std::map<String, String>::iterator iter;
4720         iter = properties.find(varname);
4721         if (iter != properties.end())
4722             {
4723             result = iter->second;
4724             }
4725         else
4726             {
4727             error("property '%s' not found", varname.c_str());
4728             return false;
4729             }
4730         }
4731     return true;
4737 /**
4738  * Analyse a string, looking for any substitutions or other
4739  * things that need resolution 
4740  */
4741 bool MakeBase::getSubstitutionsRecursive(const String &str,
4742                                          String &result, int depth)
4744     if (depth > 10)
4745         {
4746         error("nesting of substitutions too deep (>10) for '%s'",
4747                         str.c_str());
4748         return false;
4749         }
4750     String s = trim(str);
4751     int len = (int)s.size();
4752     String val;
4753     for (int i=0 ; i<len ; i++)
4754         {
4755         char ch = s[i];
4756         if (ch == '$' && s[i+1] == '{')
4757             {
4758             String varname;
4759             int j = i+2;
4760             for ( ; j<len ; j++)
4761                 {
4762                 ch = s[j];
4763                 if (ch == '$' && s[j+1] == '{')
4764                     {
4765                     error("attribute %s cannot have nested variable references",
4766                            s.c_str());
4767                     return false;
4768                     }
4769                 else if (ch == '}')
4770                     {
4771                     varname = trim(varname);
4772                     String varval;
4773                     if (!lookupProperty(varname, varval))
4774                         return false;
4775                     String varval2;
4776                     //Now see if the answer has ${} in it, too
4777                     if (!getSubstitutionsRecursive(varval, varval2, depth + 1))
4778                         return false;
4779                     val.append(varval2);
4780                     break;
4781                     }
4782                 else
4783                     {
4784                     varname.push_back(ch);
4785                     }
4786                 }
4787             i = j;
4788             }
4789         else
4790             {
4791             val.push_back(ch);
4792             }
4793         }
4794     result = val;
4795     return true;
4798 /**
4799  * Analyse a string, looking for any substitutions or other
4800  * things that need resilution 
4801  */
4802 bool MakeBase::getSubstitutions(const String &str, String &result)
4804     return getSubstitutionsRecursive(str, result, 0);
4809 /**
4810  * replace variable refs like ${a} with their values
4811  * Assume that the string has already been syntax validated
4812  */
4813 String MakeBase::eval(const String &s, const String &defaultVal)
4815     if (s.size()==0)
4816         return defaultVal;
4817     String ret;
4818     if (getSubstitutions(s, ret))
4819         return ret;
4820     else
4821         return defaultVal;
4825 /**
4826  * replace variable refs like ${a} with their values
4827  * return true or false
4828  * Assume that the string has already been syntax validated
4829  */
4830 bool MakeBase::evalBool(const String &s, bool defaultVal)
4832     if (s.size()==0)
4833         return defaultVal;
4834     String val = eval(s, "false");
4835     if (val.size()==0)
4836         return defaultVal;
4837     if (val == "true" || val == "TRUE")
4838         return true;
4839     else
4840         return false;
4844 /**
4845  * Get a string attribute, testing it for proper syntax and
4846  * property names.
4847  */
4848 bool MakeBase::getAttribute(Element *elem, const String &name,
4849                                     String &result)
4851     String s = elem->getAttribute(name);
4852     String tmp;
4853     bool ret = getSubstitutions(s, tmp);
4854     if (ret)
4855         result = s;  //assign -if- ok
4856     return ret;
4860 /**
4861  * Get a string value, testing it for proper syntax and
4862  * property names.
4863  */
4864 bool MakeBase::getValue(Element *elem, String &result)
4866     String s = elem->getValue();
4867     String tmp;
4868     bool ret = getSubstitutions(s, tmp);
4869     if (ret)
4870         result = s;  //assign -if- ok
4871     return ret;
4877 /**
4878  * Parse a <patternset> entry
4879  */  
4880 bool MakeBase::parsePatternSet(Element *elem,
4881                           MakeBase &propRef,
4882                           std::vector<String> &includes,
4883                           std::vector<String> &excludes
4884                           )
4886     std::vector<Element *> children  = elem->getChildren();
4887     for (unsigned int i=0 ; i<children.size() ; i++)
4888         {
4889         Element *child = children[i];
4890         String tagName = child->getName();
4891         if (tagName == "exclude")
4892             {
4893             String fname;
4894             if (!propRef.getAttribute(child, "name", fname))
4895                 return false;
4896             //trace("EXCLUDE: %s", fname.c_str());
4897             excludes.push_back(fname);
4898             }
4899         else if (tagName == "include")
4900             {
4901             String fname;
4902             if (!propRef.getAttribute(child, "name", fname))
4903                 return false;
4904             //trace("INCLUDE: %s", fname.c_str());
4905             includes.push_back(fname);
4906             }
4907         }
4909     return true;
4915 /**
4916  * Parse a <fileset> entry, and determine which files
4917  * should be included
4918  */  
4919 bool MakeBase::parseFileSet(Element *elem,
4920                           MakeBase &propRef,
4921                           FileSet &fileSet)
4923     String name = elem->getName();
4924     if (name != "fileset")
4925         {
4926         error("expected <fileset>");
4927         return false;
4928         }
4931     std::vector<String> includes;
4932     std::vector<String> excludes;
4934     //A fileset has one implied patternset
4935     if (!parsePatternSet(elem, propRef, includes, excludes))
4936         {
4937         return false;
4938         }
4939     //Look for child tags, including more patternsets
4940     std::vector<Element *> children  = elem->getChildren();
4941     for (unsigned int i=0 ; i<children.size() ; i++)
4942         {
4943         Element *child = children[i];
4944         String tagName = child->getName();
4945         if (tagName == "patternset")
4946             {
4947             if (!parsePatternSet(child, propRef, includes, excludes))
4948                 {
4949                 return false;
4950                 }
4951             }
4952         }
4954     String dir;
4955     //Now do the stuff
4956     //Get the base directory for reading file names
4957     if (!propRef.getAttribute(elem, "dir", dir))
4958         return false;
4960     fileSet.setDirectory(dir);
4961     fileSet.setIncludes(includes);
4962     fileSet.setExcludes(excludes);
4963     
4964     /*
4965     std::vector<String> fileList;
4966     if (dir.size() > 0)
4967         {
4968         String baseDir = propRef.resolve(dir);
4969         if (!listFiles(baseDir, "", includes, excludes, fileList))
4970             return false;
4971         }
4972     std::sort(fileList.begin(), fileList.end());
4973     result = fileList;
4974     */
4976     
4977     /*
4978     for (unsigned int i=0 ; i<result.size() ; i++)
4979         {
4980         trace("RES:%s", result[i].c_str());
4981         }
4982     */
4984     
4985     return true;
4988 /**
4989  * Parse a <filelist> entry.  This is far simpler than FileSet,
4990  * since no directory scanning is needed.  The file names are listed
4991  * explicitly.
4992  */  
4993 bool MakeBase::parseFileList(Element *elem,
4994                           MakeBase &propRef,
4995                           FileList &fileList)
4997     std::vector<String> fnames;
4998     //Look for child tags, namely "file"
4999     std::vector<Element *> children  = elem->getChildren();
5000     for (unsigned int i=0 ; i<children.size() ; i++)
5001         {
5002         Element *child = children[i];
5003         String tagName = child->getName();
5004         if (tagName == "file")
5005             {
5006             String fname = child->getAttribute("name");
5007             if (fname.size()==0)
5008                 {
5009                 error("<file> element requires name="" attribute");
5010                 return false;
5011                 }
5012             fnames.push_back(fname);
5013             }
5014         else
5015             {
5016             error("tag <%s> not allowed in <fileset>", tagName.c_str());
5017             return false;
5018             }
5019         }
5021     String dir;
5022     //Get the base directory for reading file names
5023     if (!propRef.getAttribute(elem, "dir", dir))
5024         return false;
5025     fileList.setDirectory(dir);
5026     fileList.setFiles(fnames);
5028     return true;
5033 /**
5034  * Create a directory, making intermediate dirs
5035  * if necessary
5036  */                  
5037 bool MakeBase::createDirectory(const String &dirname)
5039     //trace("## createDirectory: %s", dirname.c_str());
5040     //## first check if it exists
5041     struct stat finfo;
5042     String nativeDir = getNativePath(dirname);
5043     char *cnative = (char *) nativeDir.c_str();
5044 #ifdef __WIN32__
5045     if (strlen(cnative)==2 && cnative[1]==':')
5046         return true;
5047 #endif
5048     if (cachedStat(nativeDir, &finfo)==0)
5049         {
5050         if (!S_ISDIR(finfo.st_mode))
5051             {
5052             error("mkdir: file %s exists but is not a directory",
5053                   cnative);
5054             return false;
5055             }
5056         else //exists
5057             {
5058             return true;
5059             }
5060         }
5062     //## 2: pull off the last path segment, if any,
5063     //## to make the dir 'above' this one, if necessary
5064     unsigned int pos = dirname.find_last_of('/');
5065     if (pos>0 && pos != dirname.npos)
5066         {
5067         String subpath = dirname.substr(0, pos);
5068         //A letter root (c:) ?
5069         if (!createDirectory(subpath))
5070             return false;
5071         }
5072         
5073     //## 3: now make
5074 #ifdef __WIN32__
5075     if (mkdir(cnative)<0)
5076 #else
5077     if (mkdir(cnative, S_IRWXU | S_IRWXG | S_IRWXO)<0)
5078 #endif
5079         {
5080         error("cannot make directory '%s' : %s",
5081                  cnative, strerror(errno));
5082         return false;
5083         }
5085     removeFromStatCache(nativeDir);
5086         
5087     return true;
5091 /**
5092  * Remove a directory recursively
5093  */ 
5094 bool MakeBase::removeDirectory(const String &dirName)
5096     char *dname = (char *)dirName.c_str();
5098     DIR *dir = opendir(dname);
5099     if (!dir)
5100         {
5101         //# Let this fail nicely.
5102         return true;
5103         //error("error opening directory %s : %s", dname, strerror(errno));
5104         //return false;
5105         }
5106     
5107     while (true)
5108         {
5109         struct dirent *de = readdir(dir);
5110         if (!de)
5111             break;
5113         //Get the directory member name
5114         String s = de->d_name;
5115         if (s.size() == 0 || s[0] == '.')
5116             continue;
5117         String childName;
5118         if (dirName.size() > 0)
5119             {
5120             childName.append(dirName);
5121             childName.append("/");
5122             }
5123         childName.append(s);
5126         struct stat finfo;
5127         String childNative = getNativePath(childName);
5128         char *cnative = (char *)childNative.c_str();
5129         if (cachedStat(childNative, &finfo)<0)
5130             {
5131             error("cannot stat file:%s", cnative);
5132             }
5133         else if (S_ISDIR(finfo.st_mode))
5134             {
5135             //trace("DEL dir: %s", childName.c_str());
5136             if (!removeDirectory(childName))
5137                 {
5138                 return false;
5139                 }
5140             }
5141         else if (!S_ISREG(finfo.st_mode))
5142             {
5143             //trace("not regular: %s", cnative);
5144             }
5145         else
5146             {
5147             //trace("DEL file: %s", childName.c_str());
5148             if (!removeFile(childName))
5149                 {
5150                 return false;
5151                 }
5152             }
5153         }
5154     closedir(dir);
5156     //Now delete the directory
5157     String native = getNativePath(dirName);
5158     if (rmdir(native.c_str())<0)
5159         {
5160         error("could not delete directory %s : %s",
5161             native.c_str() , strerror(errno));
5162         return false;
5163         }
5165     removeFromStatCache(native);
5167     return true;
5168     
5172 /**
5173  * Copy a file from one name to another. Perform only if needed
5174  */ 
5175 bool MakeBase::copyFile(const String &srcFile, const String &destFile)
5177     //# 1 Check up-to-date times
5178     String srcNative = getNativePath(srcFile);
5179     struct stat srcinfo;
5180     if (cachedStat(srcNative, &srcinfo)<0)
5181         {
5182         error("source file %s for copy does not exist",
5183                  srcNative.c_str());
5184         return false;
5185         }
5187     String destNative = getNativePath(destFile);
5188     struct stat destinfo;
5189     if (cachedStat(destNative, &destinfo)==0)
5190         {
5191         if (destinfo.st_mtime >= srcinfo.st_mtime)
5192             return true;
5193         }
5194         
5195     //# 2 prepare a destination directory if necessary
5196     unsigned int pos = destFile.find_last_of('/');
5197     if (pos != destFile.npos)
5198         {
5199         String subpath = destFile.substr(0, pos);
5200         if (!createDirectory(subpath))
5201             return false;
5202         }
5204     //# 3 do the data copy
5205 #ifndef __WIN32__
5207     FILE *srcf = fopen(srcNative.c_str(), "rb");
5208     if (!srcf)
5209         {
5210         error("copyFile cannot open '%s' for reading", srcNative.c_str());
5211         return false;
5212         }
5213     FILE *destf = fopen(destNative.c_str(), "wb");
5214     if (!destf)
5215         {
5216         fclose(srcf);
5217         error("copyFile cannot open %s for writing", srcNative.c_str());
5218         return false;
5219         }
5221     while (!feof(srcf))
5222         {
5223         int ch = fgetc(srcf);
5224         if (ch<0)
5225             break;
5226         fputc(ch, destf);
5227         }
5229     fclose(destf);
5230     fclose(srcf);
5232 #else
5233     
5234     if (!CopyFile(srcNative.c_str(), destNative.c_str(), false))
5235         {
5236         error("copyFile from %s to %s failed",
5237              srcNative.c_str(), destNative.c_str());
5238         return false;
5239         }
5240         
5241 #endif /* __WIN32__ */
5243     removeFromStatCache(destNative);
5245     return true;
5249 /**
5250  * Delete a file
5251  */ 
5252 bool MakeBase::removeFile(const String &file)
5254     String native = getNativePath(file);
5256     if (!fileExists(native))
5257         {
5258         return true;
5259         }
5261 #ifdef WIN32
5262     // On Windows 'remove' will only delete files
5264     if (remove(native.c_str())<0)
5265         {
5266         if (errno==EACCES)
5267             {
5268             error("File %s is read-only", native.c_str());
5269             }
5270         else if (errno==ENOENT)
5271             {
5272             error("File %s does not exist or is a directory", native.c_str());
5273             }
5274         else
5275             {
5276             error("Failed to delete file %s: %s", native.c_str(), strerror(errno));
5277             }
5278         return false;
5279         }
5281 #else
5283     if (!isRegularFile(native))
5284         {
5285         error("File %s does not exist or is not a regular file", native.c_str());
5286         return false;
5287         }
5289     if (remove(native.c_str())<0)
5290         {
5291         if (errno==EACCES)
5292             {
5293             error("File %s is read-only", native.c_str());
5294             }
5295         else
5296             {
5297             error(
5298                 errno==EACCES ? "File %s is read-only" :
5299                 errno==ENOENT ? "File %s does not exist or is a directory" :
5300                 "Failed to delete file %s: %s", native.c_str());
5301             }
5302         return false;
5303         }
5305 #endif
5307     removeFromStatCache(native);
5309     return true;
5313 /**
5314  * Tests if the file exists
5315  */ 
5316 bool MakeBase::fileExists(const String &fileName)
5318     String native = getNativePath(fileName);
5319     struct stat finfo;
5320     
5321     //Exists?
5322     if (cachedStat(native, &finfo)<0)
5323         return false;
5325     return true;
5329 /**
5330  * Tests if the file exists and is a regular file
5331  */ 
5332 bool MakeBase::isRegularFile(const String &fileName)
5334     String native = getNativePath(fileName);
5335     struct stat finfo;
5336     
5337     //Exists?
5338     if (cachedStat(native, &finfo)<0)
5339         return false;
5342     //check the file mode
5343     if (!S_ISREG(finfo.st_mode))
5344         return false;
5346     return true;
5349 /**
5350  * Tests if the file exists and is a directory
5351  */ 
5352 bool MakeBase::isDirectory(const String &fileName)
5354     String native = getNativePath(fileName);
5355     struct stat finfo;
5356     
5357     //Exists?
5358     if (cachedStat(native, &finfo)<0)
5359         return false;
5362     //check the file mode
5363     if (!S_ISDIR(finfo.st_mode))
5364         return false;
5366     return true;
5371 /**
5372  * Tests is the modification of fileA is newer than fileB
5373  */ 
5374 bool MakeBase::isNewerThan(const String &fileA, const String &fileB)
5376     //trace("isNewerThan:'%s' , '%s'", fileA.c_str(), fileB.c_str());
5377     String nativeA = getNativePath(fileA);
5378     struct stat infoA;
5379     //IF source does not exist, NOT newer
5380     if (cachedStat(nativeA, &infoA)<0)
5381         {
5382         return false;
5383         }
5385     String nativeB = getNativePath(fileB);
5386     struct stat infoB;
5387     //IF dest does not exist, YES, newer
5388     if (cachedStat(nativeB, &infoB)<0)
5389         {
5390         return true;
5391         }
5393     //check the actual times
5394     if (infoA.st_mtime > infoB.st_mtime)
5395         {
5396         return true;
5397         }
5399     return false;
5403 //########################################################################
5404 //# P K G    C O N F I G
5405 //########################################################################
5408 /**
5409  * Get a character from the buffer at pos.  If out of range,
5410  * return -1 for safety
5411  */
5412 int PkgConfig::get(int pos)
5414     if (pos>parselen)
5415         return -1;
5416     return parsebuf[pos];
5421 /**
5422  *  Skip over all whitespace characters beginning at pos.  Return
5423  *  the position of the first non-whitespace character.
5424  *  Pkg-config is line-oriented, so check for newline
5425  */
5426 int PkgConfig::skipwhite(int pos)
5428     while (pos < parselen)
5429         {
5430         int ch = get(pos);
5431         if (ch < 0)
5432             break;
5433         if (!isspace(ch))
5434             break;
5435         pos++;
5436         }
5437     return pos;
5441 /**
5442  *  Parse the buffer beginning at pos, for a word.  Fill
5443  *  'ret' with the result.  Return the position after the
5444  *  word.
5445  */
5446 int PkgConfig::getword(int pos, String &ret)
5448     while (pos < parselen)
5449         {
5450         int ch = get(pos);
5451         if (ch < 0)
5452             break;
5453         if (!isalnum(ch) && ch != '_' && ch != '-' && ch != '+' && ch != '.')
5454             break;
5455         ret.push_back((char)ch);
5456         pos++;
5457         }
5458     return pos;
5461 bool PkgConfig::parseRequires()
5463     if (requires.size() == 0)
5464         return true;
5465     parsebuf = (char *)requires.c_str();
5466     parselen = requires.size();
5467     int pos = 0;
5468     while (pos < parselen)
5469         {
5470         pos = skipwhite(pos);
5471         String val;
5472         int pos2 = getword(pos, val);
5473         if (pos2 == pos)
5474             break;
5475         pos = pos2;
5476         //trace("val %s", val.c_str());
5477         requireList.push_back(val);
5478         }
5479     return true;
5483 static int getint(const String str)
5485     char *s = (char *)str.c_str();
5486     char *ends = NULL;
5487     long val = strtol(s, &ends, 10);
5488     if (ends == s)
5489         return 0L;
5490     else
5491         return val;
5494 void PkgConfig::parseVersion()
5496     if (version.size() == 0)
5497         return;
5498     String s1, s2, s3;
5499     unsigned int pos = 0;
5500     unsigned int pos2 = version.find('.', pos);
5501     if (pos2 == version.npos)
5502         {
5503         s1 = version;
5504         }
5505     else
5506         {
5507         s1 = version.substr(pos, pos2-pos);
5508         pos = pos2;
5509         pos++;
5510         if (pos < version.size())
5511             {
5512             pos2 = version.find('.', pos);
5513             if (pos2 == version.npos)
5514                 {
5515                 s2 = version.substr(pos, version.size()-pos);
5516                 }
5517             else
5518                 {
5519                 s2 = version.substr(pos, pos2-pos);
5520                 pos = pos2;
5521                 pos++;
5522                 if (pos < version.size())
5523                     s3 = version.substr(pos, pos2-pos);
5524                 }
5525             }
5526         }
5528     majorVersion = getint(s1);
5529     minorVersion = getint(s2);
5530     microVersion = getint(s3);
5531     //trace("version:%d.%d.%d", majorVersion,
5532     //          minorVersion, microVersion );
5536 bool PkgConfig::parseLine(const String &lineBuf)
5538     parsebuf = (char *)lineBuf.c_str();
5539     parselen = lineBuf.size();
5540     int pos = 0;
5541     
5542     while (pos < parselen)
5543         {
5544         String attrName;
5545         pos = skipwhite(pos);
5546         int ch = get(pos);
5547         if (ch == '#')
5548             {
5549             //comment.  eat the rest of the line
5550             while (pos < parselen)
5551                 {
5552                 ch = get(pos);
5553                 if (ch == '\n' || ch < 0)
5554                     break;
5555                 pos++;
5556                 }
5557             continue;
5558             }
5559         pos = getword(pos, attrName);
5560         if (attrName.size() == 0)
5561             continue;
5562         
5563         pos = skipwhite(pos);
5564         ch = get(pos);
5565         if (ch != ':' && ch != '=')
5566             {
5567             error("expected ':' or '='");
5568             return false;
5569             }
5570         pos++;
5571         pos = skipwhite(pos);
5572         String attrVal;
5573         while (pos < parselen)
5574             {
5575             ch = get(pos);
5576             if (ch == '\n' || ch < 0)
5577                 break;
5578             else if (ch == '$' && get(pos+1) == '{')
5579                 {
5580                 //#  this is a ${substitution}
5581                 pos += 2;
5582                 String subName;
5583                 while (pos < parselen)
5584                     {
5585                     ch = get(pos);
5586                     if (ch < 0)
5587                         {
5588                         error("unterminated substitution");
5589                         return false;
5590                         }
5591                     else if (ch == '}')
5592                         break;
5593                     else
5594                         subName.push_back((char)ch);
5595                     pos++;
5596                     }
5597                 //trace("subName:%s %s", subName.c_str(), prefix.c_str());
5598                 if (subName == "prefix" && prefix.size()>0)
5599                     {
5600                     attrVal.append(prefix);
5601                     //trace("prefix override:%s", prefix.c_str());
5602                     }
5603                 else
5604                     {
5605                     String subVal = attrs[subName];
5606                     //trace("subVal:%s", subVal.c_str());
5607                     attrVal.append(subVal);
5608                     }
5609                 }
5610             else
5611                 attrVal.push_back((char)ch);
5612             pos++;
5613             }
5615         attrVal = trim(attrVal);
5616         attrs[attrName] = attrVal;
5618         String attrNameL = toLower(attrName);
5620         if (attrNameL == "name")
5621             name = attrVal;
5622         else if (attrNameL == "description")
5623             description = attrVal;
5624         else if (attrNameL == "cflags")
5625             cflags = attrVal;
5626         else if (attrNameL == "libs")
5627             libs = attrVal;
5628         else if (attrNameL == "requires")
5629             requires = attrVal;
5630         else if (attrNameL == "version")
5631             version = attrVal;
5633         //trace("name:'%s'  value:'%s'",
5634         //      attrName.c_str(), attrVal.c_str());
5635         }
5637     return true;
5641 bool PkgConfig::parse(const String &buf)
5643     init();
5645     String line;
5646     int lineNr = 0;
5647     for (unsigned int p=0 ; p<buf.size() ; p++)
5648         {
5649         int ch = buf[p];
5650         if (ch == '\n' || ch == '\r')
5651             {
5652             if (!parseLine(line))
5653                 return false;
5654             line.clear();
5655             lineNr++;
5656             }
5657         else
5658             {
5659             line.push_back(ch);
5660             }
5661         }
5662     if (line.size()>0)
5663         {
5664         if (!parseLine(line))
5665             return false;
5666         }
5668     parseRequires();
5669     parseVersion();
5671     return true;
5677 void PkgConfig::dumpAttrs()
5679     //trace("### PkgConfig attributes for %s", fileName.c_str());
5680     std::map<String, String>::iterator iter;
5681     for (iter=attrs.begin() ; iter!=attrs.end() ; iter++)
5682         {
5683         trace("   %s = %s", iter->first.c_str(), iter->second.c_str());
5684         }
5688 bool PkgConfig::readFile(const String &fname)
5690     fileName = getNativePath(fname);
5692     FILE *f = fopen(fileName.c_str(), "r");
5693     if (!f)
5694         {
5695         error("cannot open file '%s' for reading", fileName.c_str());
5696         return false;
5697         }
5698     String buf;
5699     while (true)
5700         {
5701         int ch = fgetc(f);
5702         if (ch < 0)
5703             break;
5704         buf.push_back((char)ch);
5705         }
5706     fclose(f);
5708     //trace("####### File:\n%s", buf.c_str());
5709     if (!parse(buf))
5710         {
5711         return false;
5712         }
5714     //dumpAttrs();
5716     return true;
5721 bool PkgConfig::query(const String &pkgName)
5723     name = pkgName;
5725     String fname = path;
5726     fname.append("/");
5727     fname.append(name);
5728     fname.append(".pc");
5730     if (!readFile(fname))
5731         {
5732         error("Cannot find package '%s'. Do you have it installed?",
5733                        pkgName.c_str());
5734         return false;
5735         }
5736     
5737     return true;
5741 //########################################################################
5742 //# D E P T O O L
5743 //########################################################################
5747 /**
5748  *  Class which holds information for each file.
5749  */
5750 class FileRec
5752 public:
5754     typedef enum
5755         {
5756         UNKNOWN,
5757         CFILE,
5758         HFILE,
5759         OFILE
5760         } FileType;
5762     /**
5763      *  Constructor
5764      */
5765     FileRec()
5766         { init(); type = UNKNOWN; }
5768     /**
5769      *  Copy constructor
5770      */
5771     FileRec(const FileRec &other)
5772         { init(); assign(other); }
5773     /**
5774      *  Constructor
5775      */
5776     FileRec(int typeVal)
5777         { init(); type = typeVal; }
5778     /**
5779      *  Assignment operator
5780      */
5781     FileRec &operator=(const FileRec &other)
5782         { init(); assign(other); return *this; }
5785     /**
5786      *  Destructor
5787      */
5788     ~FileRec()
5789         {}
5791     /**
5792      *  Directory part of the file name
5793      */
5794     String path;
5796     /**
5797      *  Base name, sans directory and suffix
5798      */
5799     String baseName;
5801     /**
5802      *  File extension, such as cpp or h
5803      */
5804     String suffix;
5806     /**
5807      *  Type of file: CFILE, HFILE, OFILE
5808      */
5809     int type;
5811     /**
5812      * Used to list files ref'd by this one
5813      */
5814     std::map<String, FileRec *> files;
5817 private:
5819     void init()
5820         {
5821         }
5823     void assign(const FileRec &other)
5824         {
5825         type     = other.type;
5826         baseName = other.baseName;
5827         suffix   = other.suffix;
5828         files    = other.files;
5829         }
5831 };
5835 /**
5836  *  Simpler dependency record
5837  */
5838 class DepRec
5840 public:
5842     /**
5843      *  Constructor
5844      */
5845     DepRec()
5846         {init();}
5848     /**
5849      *  Copy constructor
5850      */
5851     DepRec(const DepRec &other)
5852         {init(); assign(other);}
5853     /**
5854      *  Constructor
5855      */
5856     DepRec(const String &fname)
5857         {init(); name = fname; }
5858     /**
5859      *  Assignment operator
5860      */
5861     DepRec &operator=(const DepRec &other)
5862         {init(); assign(other); return *this;}
5865     /**
5866      *  Destructor
5867      */
5868     ~DepRec()
5869         {}
5871     /**
5872      *  Directory part of the file name
5873      */
5874     String path;
5876     /**
5877      *  Base name, without the path and suffix
5878      */
5879     String name;
5881     /**
5882      *  Suffix of the source
5883      */
5884     String suffix;
5887     /**
5888      * Used to list files ref'd by this one
5889      */
5890     std::vector<String> files;
5893 private:
5895     void init()
5896         {
5897         }
5899     void assign(const DepRec &other)
5900         {
5901         path     = other.path;
5902         name     = other.name;
5903         suffix   = other.suffix;
5904         files    = other.files; //avoid recursion
5905         }
5907 };
5910 class DepTool : public MakeBase
5912 public:
5914     /**
5915      *  Constructor
5916      */
5917     DepTool()
5918         { init(); }
5920     /**
5921      *  Copy constructor
5922      */
5923     DepTool(const DepTool &other)
5924         { init(); assign(other); }
5926     /**
5927      *  Assignment operator
5928      */
5929     DepTool &operator=(const DepTool &other)
5930         { init(); assign(other); return *this; }
5933     /**
5934      *  Destructor
5935      */
5936     ~DepTool()
5937         {}
5940     /**
5941      *  Reset this section of code
5942      */
5943     virtual void init();
5944     
5945     /**
5946      *  Reset this section of code
5947      */
5948     virtual void assign(const DepTool &other)
5949         {
5950         }
5951     
5952     /**
5953      *  Sets the source directory which will be scanned
5954      */
5955     virtual void setSourceDirectory(const String &val)
5956         { sourceDir = val; }
5958     /**
5959      *  Returns the source directory which will be scanned
5960      */
5961     virtual String getSourceDirectory()
5962         { return sourceDir; }
5964     /**
5965      *  Sets the list of files within the directory to analyze
5966      */
5967     virtual void setFileList(const std::vector<String> &list)
5968         { fileList = list; }
5970     /**
5971      * Creates the list of all file names which will be
5972      * candidates for further processing.  Reads make.exclude
5973      * to see which files for directories to leave out.
5974      */
5975     virtual bool createFileList();
5978     /**
5979      *  Generates the forward dependency list
5980      */
5981     virtual bool generateDependencies();
5984     /**
5985      *  Generates the forward dependency list, saving the file
5986      */
5987     virtual bool generateDependencies(const String &);
5990     /**
5991      *  Load a dependency file
5992      */
5993     std::vector<DepRec> loadDepFile(const String &fileName);
5995     /**
5996      *  Load a dependency file, generating one if necessary
5997      */
5998     std::vector<DepRec> getDepFile(const String &fileName,
5999               bool forceRefresh);
6001     /**
6002      *  Save a dependency file
6003      */
6004     bool saveDepFile(const String &fileName);
6007 private:
6010     /**
6011      *
6012      */
6013     void parseName(const String &fullname,
6014                    String &path,
6015                    String &basename,
6016                    String &suffix);
6018     /**
6019      *
6020      */
6021     int get(int pos);
6023     /**
6024      *
6025      */
6026     int skipwhite(int pos);
6028     /**
6029      *
6030      */
6031     int getword(int pos, String &ret);
6033     /**
6034      *
6035      */
6036     bool sequ(int pos, const char *key);
6038     /**
6039      *
6040      */
6041     bool addIncludeFile(FileRec *frec, const String &fname);
6043     /**
6044      *
6045      */
6046     bool scanFile(const String &fname, FileRec *frec);
6048     /**
6049      *
6050      */
6051     bool processDependency(FileRec *ofile, FileRec *include);
6053     /**
6054      *
6055      */
6056     String sourceDir;
6058     /**
6059      *
6060      */
6061     std::vector<String> fileList;
6063     /**
6064      *
6065      */
6066     std::vector<String> directories;
6068     /**
6069      * A list of all files which will be processed for
6070      * dependencies.
6071      */
6072     std::map<String, FileRec *> allFiles;
6074     /**
6075      * The list of .o files, and the
6076      * dependencies upon them.
6077      */
6078     std::map<String, FileRec *> oFiles;
6080     int depFileSize;
6081     char *depFileBuf;
6083     static const int readBufSize = 8192;
6084     char readBuf[8193];//byte larger
6086 };
6092 /**
6093  *  Clean up after processing.  Called by the destructor, but should
6094  *  also be called before the object is reused.
6095  */
6096 void DepTool::init()
6098     sourceDir = ".";
6100     fileList.clear();
6101     directories.clear();
6102     
6103     //clear output file list
6104     std::map<String, FileRec *>::iterator iter;
6105     for (iter=oFiles.begin(); iter!=oFiles.end() ; iter++)
6106         delete iter->second;
6107     oFiles.clear();
6109     //allFiles actually contains the master copies. delete them
6110     for (iter= allFiles.begin(); iter!=allFiles.end() ; iter++)
6111         delete iter->second;
6112     allFiles.clear(); 
6119 /**
6120  *  Parse a full path name into path, base name, and suffix
6121  */
6122 void DepTool::parseName(const String &fullname,
6123                         String &path,
6124                         String &basename,
6125                         String &suffix)
6127     if (fullname.size() < 2)
6128         return;
6130     unsigned int pos = fullname.find_last_of('/');
6131     if (pos != fullname.npos && pos<fullname.size()-1)
6132         {
6133         path = fullname.substr(0, pos);
6134         pos++;
6135         basename = fullname.substr(pos, fullname.size()-pos);
6136         }
6137     else
6138         {
6139         path = "";
6140         basename = fullname;
6141         }
6143     pos = basename.find_last_of('.');
6144     if (pos != basename.npos && pos<basename.size()-1)
6145         {
6146         suffix   = basename.substr(pos+1, basename.size()-pos-1);
6147         basename = basename.substr(0, pos);
6148         }
6150     //trace("parsename:%s %s %s", path.c_str(),
6151     //        basename.c_str(), suffix.c_str()); 
6156 /**
6157  *  Generate our internal file list.
6158  */
6159 bool DepTool::createFileList()
6162     for (unsigned int i=0 ; i<fileList.size() ; i++)
6163         {
6164         String fileName = fileList[i];
6165         //trace("## FileName:%s", fileName.c_str());
6166         String path;
6167         String basename;
6168         String sfx;
6169         parseName(fileName, path, basename, sfx);
6170         if (sfx == "cpp" || sfx == "c" || sfx == "cxx"   ||
6171             sfx == "cc" || sfx == "CC")
6172             {
6173             FileRec *fe         = new FileRec(FileRec::CFILE);
6174             fe->path            = path;
6175             fe->baseName        = basename;
6176             fe->suffix          = sfx;
6177             allFiles[fileName]  = fe;
6178             }
6179         else if (sfx == "h"   ||  sfx == "hh"  ||
6180                  sfx == "hpp" ||  sfx == "hxx")
6181             {
6182             FileRec *fe         = new FileRec(FileRec::HFILE);
6183             fe->path            = path;
6184             fe->baseName        = basename;
6185             fe->suffix          = sfx;
6186             allFiles[fileName]  = fe;
6187             }
6188         }
6190     if (!listDirectories(sourceDir, "", directories))
6191         return false;
6192         
6193     return true;
6200 /**
6201  * Get a character from the buffer at pos.  If out of range,
6202  * return -1 for safety
6203  */
6204 int DepTool::get(int pos)
6206     if (pos>depFileSize)
6207         return -1;
6208     return depFileBuf[pos];
6213 /**
6214  *  Skip over all whitespace characters beginning at pos.  Return
6215  *  the position of the first non-whitespace character.
6216  */
6217 int DepTool::skipwhite(int pos)
6219     while (pos < depFileSize)
6220         {
6221         int ch = get(pos);
6222         if (ch < 0)
6223             break;
6224         if (!isspace(ch))
6225             break;
6226         pos++;
6227         }
6228     return pos;
6232 /**
6233  *  Parse the buffer beginning at pos, for a word.  Fill
6234  *  'ret' with the result.  Return the position after the
6235  *  word.
6236  */
6237 int DepTool::getword(int pos, String &ret)
6239     while (pos < depFileSize)
6240         {
6241         int ch = get(pos);
6242         if (ch < 0)
6243             break;
6244         if (isspace(ch))
6245             break;
6246         ret.push_back((char)ch);
6247         pos++;
6248         }
6249     return pos;
6252 /**
6253  * Return whether the sequence of characters in the buffer
6254  * beginning at pos match the key,  for the length of the key
6255  */
6256 bool DepTool::sequ(int pos, const char *key)
6258     while (*key)
6259         {
6260         if (*key != get(pos))
6261             return false;
6262         key++; pos++;
6263         }
6264     return true;
6269 /**
6270  *  Add an include file name to a file record.  If the name
6271  *  is not found in allFiles explicitly, try prepending include
6272  *  directory names to it and try again.
6273  */
6274 bool DepTool::addIncludeFile(FileRec *frec, const String &iname)
6276     //# if the name is an exact match to a path name
6277     //# in allFiles, like "myinc.h"
6278     std::map<String, FileRec *>::iterator iter =
6279            allFiles.find(iname);
6280     if (iter != allFiles.end()) //already exists
6281         {
6282          //h file in same dir
6283         FileRec *other = iter->second;
6284         //trace("local: '%s'", iname.c_str());
6285         frec->files[iname] = other;
6286         return true;
6287         }
6288     else 
6289         {
6290         //## Ok, it was not found directly
6291         //look in other dirs
6292         std::vector<String>::iterator diter;
6293         for (diter=directories.begin() ;
6294              diter!=directories.end() ; diter++)
6295             {
6296             String dfname = *diter;
6297             dfname.append("/");
6298             dfname.append(iname);
6299             URI fullPathURI(dfname);  //normalize path name
6300             String fullPath = fullPathURI.getPath();
6301             if (fullPath[0] == '/')
6302                 fullPath = fullPath.substr(1);
6303             //trace("Normalized %s to %s", dfname.c_str(), fullPath.c_str());
6304             iter = allFiles.find(fullPath);
6305             if (iter != allFiles.end())
6306                 {
6307                 FileRec *other = iter->second;
6308                 //trace("other: '%s'", iname.c_str());
6309                 frec->files[fullPath] = other;
6310                 return true;
6311                 }
6312             }
6313         }
6314     return true;
6319 /**
6320  *  Lightly parse a file to find the #include directives.  Do
6321  *  a bit of state machine stuff to make sure that the directive
6322  *  is valid.  (Like not in a comment).
6323  */
6324 bool DepTool::scanFile(const String &fname, FileRec *frec)
6326     String fileName;
6327     if (sourceDir.size() > 0)
6328         {
6329         fileName.append(sourceDir);
6330         fileName.append("/");
6331         }
6332     fileName.append(fname);
6333     String nativeName = getNativePath(fileName);
6334     FILE *f = fopen(nativeName.c_str(), "r");
6335     if (!f)
6336         {
6337         error("Could not open '%s' for reading", fname.c_str());
6338         return false;
6339         }
6340     String buf;
6341     while (!feof(f))
6342         {
6343         int nrbytes = fread(readBuf, 1, readBufSize, f);
6344         readBuf[nrbytes] = '\0';
6345         buf.append(readBuf);
6346         }
6347     fclose(f);
6349     depFileSize = buf.size();
6350     depFileBuf  = (char *)buf.c_str();
6351     int pos = 0;
6354     while (pos < depFileSize)
6355         {
6356         //trace("p:%c", get(pos));
6358         //# Block comment
6359         if (get(pos) == '/' && get(pos+1) == '*')
6360             {
6361             pos += 2;
6362             while (pos < depFileSize)
6363                 {
6364                 if (get(pos) == '*' && get(pos+1) == '/')
6365                     {
6366                     pos += 2;
6367                     break;
6368                     }
6369                 else
6370                     pos++;
6371                 }
6372             }
6373         //# Line comment
6374         else if (get(pos) == '/' && get(pos+1) == '/')
6375             {
6376             pos += 2;
6377             while (pos < depFileSize)
6378                 {
6379                 if (get(pos) == '\n')
6380                     {
6381                     pos++;
6382                     break;
6383                     }
6384                 else
6385                     pos++;
6386                 }
6387             }
6388         //# #include! yaay
6389         else if (sequ(pos, "#include"))
6390             {
6391             pos += 8;
6392             pos = skipwhite(pos);
6393             String iname;
6394             pos = getword(pos, iname);
6395             if (iname.size()>2)
6396                 {
6397                 iname = iname.substr(1, iname.size()-2);
6398                 addIncludeFile(frec, iname);
6399                 }
6400             }
6401         else
6402             {
6403             pos++;
6404             }
6405         }
6407     return true;
6412 /**
6413  *  Recursively check include lists to find all files in allFiles to which
6414  *  a given file is dependent.
6415  */
6416 bool DepTool::processDependency(FileRec *ofile, FileRec *include)
6418     std::map<String, FileRec *>::iterator iter;
6419     for (iter=include->files.begin() ; iter!=include->files.end() ; iter++)
6420         {
6421         String fname  = iter->first;
6422         if (ofile->files.find(fname) != ofile->files.end())
6423             {
6424             //trace("file '%s' already seen", fname.c_str());
6425             continue;
6426             }
6427         FileRec *child  = iter->second;
6428         ofile->files[fname] = child;
6429       
6430         processDependency(ofile, child);
6431         }
6434     return true;
6441 /**
6442  *  Generate the file dependency list.
6443  */
6444 bool DepTool::generateDependencies()
6446     std::map<String, FileRec *>::iterator iter;
6447     //# First pass.  Scan for all includes
6448     for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
6449         {
6450         FileRec *frec = iter->second;
6451         if (!scanFile(iter->first, frec))
6452             {
6453             //quit?
6454             }
6455         }
6457     //# Second pass.  Scan for all includes
6458     for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
6459         {
6460         FileRec *include = iter->second;
6461         if (include->type == FileRec::CFILE)
6462             {
6463             //String cFileName   = iter->first;
6464             FileRec *ofile     = new FileRec(FileRec::OFILE);
6465             ofile->path        = include->path;
6466             ofile->baseName    = include->baseName;
6467             ofile->suffix      = include->suffix;
6468             String fname       = include->path;
6469             if (fname.size()>0)
6470                 fname.append("/");
6471             fname.append(include->baseName);
6472             fname.append(".o");
6473             oFiles[fname]    = ofile;
6474             //add the .c file first?   no, don't
6475             //ofile->files[cFileName] = include;
6476             
6477             //trace("ofile:%s", fname.c_str());
6479             processDependency(ofile, include);
6480             }
6481         }
6483       
6484     return true;
6489 /**
6490  *  High-level call to generate deps and optionally save them
6491  */
6492 bool DepTool::generateDependencies(const String &fileName)
6494     if (!createFileList())
6495         return false;
6496     if (!generateDependencies())
6497         return false;
6498     if (!saveDepFile(fileName))
6499         return false;
6500     return true;
6504 /**
6505  *   This saves the dependency cache.
6506  */
6507 bool DepTool::saveDepFile(const String &fileName)
6509     time_t tim;
6510     time(&tim);
6512     FILE *f = fopen(fileName.c_str(), "w");
6513     if (!f)
6514         {
6515         trace("cannot open '%s' for writing", fileName.c_str());
6516         }
6517     fprintf(f, "<?xml version='1.0'?>\n");
6518     fprintf(f, "<!--\n");
6519     fprintf(f, "########################################################\n");
6520     fprintf(f, "## File: build.dep\n");
6521     fprintf(f, "## Generated by BuildTool at :%s", ctime(&tim));
6522     fprintf(f, "########################################################\n");
6523     fprintf(f, "-->\n");
6525     fprintf(f, "<dependencies source='%s'>\n\n", sourceDir.c_str());
6526     std::map<String, FileRec *>::iterator iter;
6527     for (iter=oFiles.begin() ; iter!=oFiles.end() ; iter++)
6528         {
6529         FileRec *frec = iter->second;
6530         if (frec->type == FileRec::OFILE)
6531             {
6532             fprintf(f, "<object path='%s' name='%s' suffix='%s'>\n",
6533                  frec->path.c_str(), frec->baseName.c_str(), frec->suffix.c_str());
6534             std::map<String, FileRec *>::iterator citer;
6535             for (citer=frec->files.begin() ; citer!=frec->files.end() ; citer++)
6536                 {
6537                 String cfname = citer->first;
6538                 fprintf(f, "    <dep name='%s'/>\n", cfname.c_str());
6539                 }
6540             fprintf(f, "</object>\n\n");
6541             }
6542         }
6544     fprintf(f, "</dependencies>\n");
6545     fprintf(f, "\n");
6546     fprintf(f, "<!--\n");
6547     fprintf(f, "########################################################\n");
6548     fprintf(f, "## E N D\n");
6549     fprintf(f, "########################################################\n");
6550     fprintf(f, "-->\n");
6552     fclose(f);
6554     return true;
6560 /**
6561  *   This loads the dependency cache.
6562  */
6563 std::vector<DepRec> DepTool::loadDepFile(const String &depFile)
6565     std::vector<DepRec> result;
6566     
6567     Parser parser;
6568     Element *root = parser.parseFile(depFile.c_str());
6569     if (!root)
6570         {
6571         //error("Could not open %s for reading", depFile.c_str());
6572         return result;
6573         }
6575     if (root->getChildren().size()==0 ||
6576         root->getChildren()[0]->getName()!="dependencies")
6577         {
6578         error("loadDepFile: main xml element should be <dependencies>");
6579         delete root;
6580         return result;
6581         }
6583     //########## Start parsing
6584     Element *depList = root->getChildren()[0];
6586     std::vector<Element *> objects = depList->getChildren();
6587     for (unsigned int i=0 ; i<objects.size() ; i++)
6588         {
6589         Element *objectElem = objects[i];
6590         String tagName = objectElem->getName();
6591         if (tagName != "object")
6592             {
6593             error("loadDepFile: <dependencies> should have only <object> children");
6594             return result;
6595             }
6597         String objName   = objectElem->getAttribute("name");
6598          //trace("object:%s", objName.c_str());
6599         DepRec depObject(objName);
6600         depObject.path   = objectElem->getAttribute("path");
6601         depObject.suffix = objectElem->getAttribute("suffix");
6602         //########## DESCRIPTION
6603         std::vector<Element *> depElems = objectElem->getChildren();
6604         for (unsigned int i=0 ; i<depElems.size() ; i++)
6605             {
6606             Element *depElem = depElems[i];
6607             tagName = depElem->getName();
6608             if (tagName != "dep")
6609                 {
6610                 error("loadDepFile: <object> should have only <dep> children");
6611                 return result;
6612                 }
6613             String depName = depElem->getAttribute("name");
6614             //trace("    dep:%s", depName.c_str());
6615             depObject.files.push_back(depName);
6616             }
6618         //Insert into the result list, in a sorted manner
6619         bool inserted = false;
6620         std::vector<DepRec>::iterator iter;
6621         for (iter = result.begin() ; iter != result.end() ; iter++)
6622             {
6623             String vpath = iter->path;
6624             vpath.append("/");
6625             vpath.append(iter->name);
6626             String opath = depObject.path;
6627             opath.append("/");
6628             opath.append(depObject.name);
6629             if (vpath > opath)
6630                 {
6631                 inserted = true;
6632                 iter = result.insert(iter, depObject);
6633                 break;
6634                 }
6635             }
6636         if (!inserted)
6637             result.push_back(depObject);
6638         }
6640     delete root;
6642     return result;
6646 /**
6647  *   This loads the dependency cache.
6648  */
6649 std::vector<DepRec> DepTool::getDepFile(const String &depFile,
6650                    bool forceRefresh)
6652     std::vector<DepRec> result;
6653     if (forceRefresh)
6654         {
6655         generateDependencies(depFile);
6656         result = loadDepFile(depFile);
6657         }
6658     else
6659         {
6660         //try once
6661         result = loadDepFile(depFile);
6662         if (result.size() == 0)
6663             {
6664             //fail? try again
6665             generateDependencies(depFile);
6666             result = loadDepFile(depFile);
6667             }
6668         }
6669     return result;
6675 //########################################################################
6676 //# T A S K
6677 //########################################################################
6678 //forward decl
6679 class Target;
6680 class Make;
6682 /**
6683  *
6684  */
6685 class Task : public MakeBase
6688 public:
6690     typedef enum
6691         {
6692         TASK_NONE,
6693         TASK_CC,
6694         TASK_COPY,
6695         TASK_CXXTEST_PART,
6696         TASK_CXXTEST_ROOT,
6697         TASK_CXXTEST_RUN,
6698         TASK_DELETE,
6699         TASK_ECHO,
6700         TASK_JAR,
6701         TASK_JAVAC,
6702         TASK_LINK,
6703         TASK_MAKEFILE,
6704         TASK_MKDIR,
6705         TASK_MSGFMT,
6706         TASK_PKG_CONFIG,
6707         TASK_RANLIB,
6708         TASK_RC,
6709         TASK_SHAREDLIB,
6710         TASK_STATICLIB,
6711         TASK_STRIP,
6712         TASK_TOUCH,
6713         TASK_TSTAMP
6714         } TaskType;
6715         
6717     /**
6718      *
6719      */
6720     Task(MakeBase &par) : parent(par)
6721         { init(); }
6723     /**
6724      *
6725      */
6726     Task(const Task &other) : parent(other.parent)
6727         { init(); assign(other); }
6729     /**
6730      *
6731      */
6732     Task &operator=(const Task &other)
6733         { assign(other); return *this; }
6735     /**
6736      *
6737      */
6738     virtual ~Task()
6739         { }
6742     /**
6743      *
6744      */
6745     virtual MakeBase &getParent()
6746         { return parent; }
6748      /**
6749      *
6750      */
6751     virtual int  getType()
6752         { return type; }
6754     /**
6755      *
6756      */
6757     virtual void setType(int val)
6758         { type = val; }
6760     /**
6761      *
6762      */
6763     virtual String getName()
6764         { return name; }
6766     /**
6767      *
6768      */
6769     virtual bool execute()
6770         { return true; }
6772     /**
6773      *
6774      */
6775     virtual bool parse(Element *elem)
6776         { return true; }
6778     /**
6779      *
6780      */
6781     Task *createTask(Element *elem, int lineNr);
6784 protected:
6786     void init()
6787         {
6788         type = TASK_NONE;
6789         name = "none";
6790         }
6792     void assign(const Task &other)
6793         {
6794         type = other.type;
6795         name = other.name;
6796         }
6797         
6798     /**
6799      *  Show task status
6800      */
6801     void taskstatus(const char *fmt, ...)
6802         {
6803         va_list args;
6804         va_start(args,fmt);
6805         fprintf(stdout, "    %s : ", name.c_str());
6806         vfprintf(stdout, fmt, args);
6807         fprintf(stdout, "\n");
6808         va_end(args) ;
6809         }
6811     String getAttribute(Element *elem, const String &attrName)
6812         {
6813         String str;
6814         return str;
6815         }
6817     MakeBase &parent;
6819     int type;
6821     String name;
6822 };
6826 /**
6827  * This task runs the C/C++ compiler.  The compiler is invoked
6828  * for all .c or .cpp files which are newer than their correcsponding
6829  * .o files.  
6830  */
6831 class TaskCC : public Task
6833 public:
6835     TaskCC(MakeBase &par) : Task(par)
6836         {
6837         type = TASK_CC;
6838         name = "cc";
6839         }
6841     virtual ~TaskCC()
6842         {}
6843         
6844     virtual bool isExcludedInc(const String &dirname)
6845         {
6846         for (unsigned int i=0 ; i<excludeInc.size() ; i++)
6847             {
6848             String fname = excludeInc[i];
6849             if (fname == dirname)
6850                 return true;
6851             }
6852         return false;
6853         }
6855     virtual bool execute()
6856         {
6857         //evaluate our parameters
6858         String command         = parent.eval(commandOpt, "gcc");
6859         String ccCommand       = parent.eval(ccCommandOpt, "gcc");
6860         String cxxCommand      = parent.eval(cxxCommandOpt, "g++");
6861         String source          = parent.eval(sourceOpt, ".");
6862         String dest            = parent.eval(destOpt, ".");
6863         String flags           = parent.eval(flagsOpt, "");
6864         String defines         = parent.eval(definesOpt, "");
6865         String includes        = parent.eval(includesOpt, "");
6866         bool continueOnError   = parent.evalBool(continueOnErrorOpt, true);
6867         bool refreshCache      = parent.evalBool(refreshCacheOpt, false);
6869         if (!listFiles(parent, fileSet))
6870             return false;
6871             
6872         FILE *f = NULL;
6873         f = fopen("compile.lst", "w");
6875         //refreshCache is probably false here, unless specified otherwise
6876         String fullName = parent.resolve("build.dep");
6877         if (refreshCache || isNewerThan(parent.getURI().getPath(), fullName))
6878             {
6879             taskstatus("regenerating C/C++ dependency cache");
6880             refreshCache = true;
6881             }
6883         DepTool depTool;
6884         depTool.setSourceDirectory(source);
6885         depTool.setFileList(fileSet.getFiles());
6886         std::vector<DepRec> deps =
6887              depTool.getDepFile("build.dep", refreshCache);
6888         
6889         String incs;
6890         incs.append("-I");
6891         incs.append(parent.resolve("."));
6892         incs.append(" ");
6893         if (includes.size()>0)
6894             {
6895             incs.append(includes);
6896             incs.append(" ");
6897             }
6898         std::set<String> paths;
6899         std::vector<DepRec>::iterator viter;
6900         for (viter=deps.begin() ; viter!=deps.end() ; viter++)
6901             {
6902             DepRec dep = *viter;
6903             if (dep.path.size()>0)
6904                 paths.insert(dep.path);
6905             }
6906         if (source.size()>0)
6907             {
6908             incs.append(" -I");
6909             incs.append(parent.resolve(source));
6910             incs.append(" ");
6911             }
6912         std::set<String>::iterator setIter;
6913         for (setIter=paths.begin() ; setIter!=paths.end() ; setIter++)
6914             {
6915             String dirName = *setIter;
6916             //check excludeInc to see if we dont want to include this dir
6917             if (isExcludedInc(dirName))
6918                 continue;
6919             incs.append(" -I");
6920             String dname;
6921             if (source.size()>0)
6922                 {
6923                 dname.append(source);
6924                 dname.append("/");
6925                 }
6926             dname.append(dirName);
6927             incs.append(parent.resolve(dname));
6928             }
6929             
6930         /**
6931          * Compile each of the C files that need it
6932          */
6933         bool errorOccurred = false;                 
6934         std::vector<String> cfiles;
6935         for (viter=deps.begin() ; viter!=deps.end() ; viter++)
6936             {
6937             DepRec dep = *viter;
6939             //## Select command
6940             String sfx = dep.suffix;
6941             String command = ccCommand;
6942             if (sfx == "cpp" || sfx == "cxx" || sfx == "c++" ||
6943                  sfx == "cc" || sfx == "CC")
6944                 command = cxxCommand;
6945  
6946             //## Make paths
6947             String destPath = dest;
6948             String srcPath  = source;
6949             if (dep.path.size()>0)
6950                 {
6951                 destPath.append("/");
6952                 destPath.append(dep.path);
6953                 srcPath.append("/");
6954                 srcPath.append(dep.path);
6955                 }
6956             //## Make sure destination directory exists
6957             if (!createDirectory(destPath))
6958                 return false;
6959                 
6960             //## Check whether it needs to be done
6961             String destName;
6962             if (destPath.size()>0)
6963                 {
6964                 destName.append(destPath);
6965                 destName.append("/");
6966                 }
6967             destName.append(dep.name);
6968             destName.append(".o");
6969             String destFullName = parent.resolve(destName);
6970             String srcName;
6971             if (srcPath.size()>0)
6972                 {
6973                 srcName.append(srcPath);
6974                 srcName.append("/");
6975                 }
6976             srcName.append(dep.name);
6977             srcName.append(".");
6978             srcName.append(dep.suffix);
6979             String srcFullName = parent.resolve(srcName);
6980             bool compileMe = false;
6981             //# First we check if the source is newer than the .o
6982             if (isNewerThan(srcFullName, destFullName))
6983                 {
6984                 taskstatus("compile of %s required by source: %s",
6985                         destFullName.c_str(), srcFullName.c_str());
6986                 compileMe = true;
6987                 }
6988             else
6989                 {
6990                 //# secondly, we check if any of the included dependencies
6991                 //# of the .c/.cpp is newer than the .o
6992                 for (unsigned int i=0 ; i<dep.files.size() ; i++)
6993                     {
6994                     String depName;
6995                     if (source.size()>0)
6996                         {
6997                         depName.append(source);
6998                         depName.append("/");
6999                         }
7000                     depName.append(dep.files[i]);
7001                     String depFullName = parent.resolve(depName);
7002                     bool depRequires = isNewerThan(depFullName, destFullName);
7003                     //trace("%d %s %s\n", depRequires,
7004                     //        destFullName.c_str(), depFullName.c_str());
7005                     if (depRequires)
7006                         {
7007                         taskstatus("compile of %s required by included: %s",
7008                                 destFullName.c_str(), depFullName.c_str());
7009                         compileMe = true;
7010                         break;
7011                         }
7012                     }
7013                 }
7014             if (!compileMe)
7015                 {
7016                 continue;
7017                 }
7019             //## Assemble the command
7020             String cmd = command;
7021             cmd.append(" -c ");
7022             cmd.append(flags);
7023             cmd.append(" ");
7024             cmd.append(defines);
7025             cmd.append(" ");
7026             cmd.append(incs);
7027             cmd.append(" ");
7028             cmd.append(srcFullName);
7029             cmd.append(" -o ");
7030             cmd.append(destFullName);
7032             //## Execute the command
7034             String outString, errString;
7035             bool ret = executeCommand(cmd.c_str(), "", outString, errString);
7037             if (f)
7038                 {
7039                 fprintf(f, "########################### File : %s\n",
7040                              srcFullName.c_str());
7041                 fprintf(f, "#### COMMAND ###\n");
7042                 int col = 0;
7043                 for (unsigned int i = 0 ; i < cmd.size() ; i++)
7044                     {
7045                     char ch = cmd[i];
7046                     if (isspace(ch)  && col > 63)
7047                         {
7048                         fputc('\n', f);
7049                         col = 0;
7050                         }
7051                     else
7052                         {
7053                         fputc(ch, f);
7054                         col++;
7055                         }
7056                     if (col > 76)
7057                         {
7058                         fputc('\n', f);
7059                         col = 0;
7060                         }
7061                     }
7062                 fprintf(f, "\n");
7063                 fprintf(f, "#### STDOUT ###\n%s\n", outString.c_str());
7064                 fprintf(f, "#### STDERR ###\n%s\n\n", errString.c_str());
7065                 fflush(f);
7066                 }
7067             if (!ret)
7068                 {
7069                 error("problem compiling: %s", errString.c_str());
7070                 errorOccurred = true;
7071                 }
7072             if (errorOccurred && !continueOnError)
7073                 break;
7075             removeFromStatCache(getNativePath(destFullName));
7076             }
7078         if (f)
7079             {
7080             fclose(f);
7081             }
7082         
7083         return !errorOccurred;
7084         }
7087     virtual bool parse(Element *elem)
7088         {
7089         String s;
7090         if (!parent.getAttribute(elem, "command", commandOpt))
7091             return false;
7092         if (commandOpt.size()>0)
7093             { cxxCommandOpt = ccCommandOpt = commandOpt; }
7094         if (!parent.getAttribute(elem, "cc", ccCommandOpt))
7095             return false;
7096         if (!parent.getAttribute(elem, "cxx", cxxCommandOpt))
7097             return false;
7098         if (!parent.getAttribute(elem, "destdir", destOpt))
7099             return false;
7100         if (!parent.getAttribute(elem, "continueOnError", continueOnErrorOpt))
7101             return false;
7102         if (!parent.getAttribute(elem, "refreshCache", refreshCacheOpt))
7103             return false;
7105         std::vector<Element *> children = elem->getChildren();
7106         for (unsigned int i=0 ; i<children.size() ; i++)
7107             {
7108             Element *child = children[i];
7109             String tagName = child->getName();
7110             if (tagName == "flags")
7111                 {
7112                 if (!parent.getValue(child, flagsOpt))
7113                     return false;
7114                 flagsOpt = strip(flagsOpt);
7115                 }
7116             else if (tagName == "includes")
7117                 {
7118                 if (!parent.getValue(child, includesOpt))
7119                     return false;
7120                 includesOpt = strip(includesOpt);
7121                 }
7122             else if (tagName == "defines")
7123                 {
7124                 if (!parent.getValue(child, definesOpt))
7125                     return false;
7126                 definesOpt = strip(definesOpt);
7127                 }
7128             else if (tagName == "fileset")
7129                 {
7130                 if (!parseFileSet(child, parent, fileSet))
7131                     return false;
7132                 sourceOpt = fileSet.getDirectory();
7133                 }
7134             else if (tagName == "excludeinc")
7135                 {
7136                 if (!parseFileList(child, parent, excludeInc))
7137                     return false;
7138                 }
7139             }
7141         return true;
7142         }
7143         
7144 protected:
7146     String   commandOpt;
7147     String   ccCommandOpt;
7148     String   cxxCommandOpt;
7149     String   sourceOpt;
7150     String   destOpt;
7151     String   flagsOpt;
7152     String   definesOpt;
7153     String   includesOpt;
7154     String   continueOnErrorOpt;
7155     String   refreshCacheOpt;
7156     FileSet  fileSet;
7157     FileList excludeInc;
7158     
7159 };
7163 /**
7164  *
7165  */
7166 class TaskCopy : public Task
7168 public:
7170     typedef enum
7171         {
7172         CP_NONE,
7173         CP_TOFILE,
7174         CP_TODIR
7175         } CopyType;
7177     TaskCopy(MakeBase &par) : Task(par)
7178         {
7179         type        = TASK_COPY;
7180         name        = "copy";
7181         cptype      = CP_NONE;
7182         haveFileSet = false;
7183         }
7185     virtual ~TaskCopy()
7186         {}
7188     virtual bool execute()
7189         {
7190         String fileName   = parent.eval(fileNameOpt   , ".");
7191         String toFileName = parent.eval(toFileNameOpt , ".");
7192         String toDirName  = parent.eval(toDirNameOpt  , ".");
7193         bool   verbose    = parent.evalBool(verboseOpt, false);
7194         switch (cptype)
7195            {
7196            case CP_TOFILE:
7197                {
7198                if (fileName.size()>0)
7199                    {
7200                    taskstatus("%s to %s",
7201                         fileName.c_str(), toFileName.c_str());
7202                    String fullSource = parent.resolve(fileName);
7203                    String fullDest = parent.resolve(toFileName);
7204                    if (verbose)
7205                        taskstatus("copy %s to file %s", fullSource.c_str(),
7206                                           fullDest.c_str());
7207                    if (!isRegularFile(fullSource))
7208                        {
7209                        error("copy : file %s does not exist", fullSource.c_str());
7210                        return false;
7211                        }
7212                    if (!isNewerThan(fullSource, fullDest))
7213                        {
7214                        taskstatus("skipped");
7215                        return true;
7216                        }
7217                    if (!copyFile(fullSource, fullDest))
7218                        return false;
7219                    taskstatus("1 file copied");
7220                    }
7221                return true;
7222                }
7223            case CP_TODIR:
7224                {
7225                if (haveFileSet)
7226                    {
7227                    if (!listFiles(parent, fileSet))
7228                        return false;
7229                    String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
7231                    taskstatus("%s to %s",
7232                        fileSetDir.c_str(), toDirName.c_str());
7234                    int nrFiles = 0;
7235                    for (unsigned int i=0 ; i<fileSet.size() ; i++)
7236                        {
7237                        String fileName = fileSet[i];
7239                        String sourcePath;
7240                        if (fileSetDir.size()>0)
7241                            {
7242                            sourcePath.append(fileSetDir);
7243                            sourcePath.append("/");
7244                            }
7245                        sourcePath.append(fileName);
7246                        String fullSource = parent.resolve(sourcePath);
7247                        
7248                        //Get the immediate parent directory's base name
7249                        String baseFileSetDir = fileSetDir;
7250                        unsigned int pos = baseFileSetDir.find_last_of('/');
7251                        if (pos!=baseFileSetDir.npos &&
7252                                   pos < baseFileSetDir.size()-1)
7253                            baseFileSetDir =
7254                               baseFileSetDir.substr(pos+1,
7255                                    baseFileSetDir.size());
7256                        //Now make the new path
7257                        String destPath;
7258                        if (toDirName.size()>0)
7259                            {
7260                            destPath.append(toDirName);
7261                            destPath.append("/");
7262                            }
7263                        if (baseFileSetDir.size()>0)
7264                            {
7265                            destPath.append(baseFileSetDir);
7266                            destPath.append("/");
7267                            }
7268                        destPath.append(fileName);
7269                        String fullDest = parent.resolve(destPath);
7270                        //trace("fileName:%s", fileName.c_str());
7271                        if (verbose)
7272                            taskstatus("copy %s to new dir : %s",
7273                                  fullSource.c_str(), fullDest.c_str());
7274                        if (!isNewerThan(fullSource, fullDest))
7275                            {
7276                            if (verbose)
7277                                taskstatus("copy skipping %s", fullSource.c_str());
7278                            continue;
7279                            }
7280                        if (!copyFile(fullSource, fullDest))
7281                            return false;
7282                        nrFiles++;
7283                        }
7284                    taskstatus("%d file(s) copied", nrFiles);
7285                    }
7286                else //file source
7287                    {
7288                    //For file->dir we want only the basename of
7289                    //the source appended to the dest dir
7290                    taskstatus("%s to %s", 
7291                        fileName.c_str(), toDirName.c_str());
7292                    String baseName = fileName;
7293                    unsigned int pos = baseName.find_last_of('/');
7294                    if (pos!=baseName.npos && pos<baseName.size()-1)
7295                        baseName = baseName.substr(pos+1, baseName.size());
7296                    String fullSource = parent.resolve(fileName);
7297                    String destPath;
7298                    if (toDirName.size()>0)
7299                        {
7300                        destPath.append(toDirName);
7301                        destPath.append("/");
7302                        }
7303                    destPath.append(baseName);
7304                    String fullDest = parent.resolve(destPath);
7305                    if (verbose)
7306                        taskstatus("file %s to new dir : %s", fullSource.c_str(),
7307                                           fullDest.c_str());
7308                    if (!isRegularFile(fullSource))
7309                        {
7310                        error("copy : file %s does not exist", fullSource.c_str());
7311                        return false;
7312                        }
7313                    if (!isNewerThan(fullSource, fullDest))
7314                        {
7315                        taskstatus("skipped");
7316                        return true;
7317                        }
7318                    if (!copyFile(fullSource, fullDest))
7319                        return false;
7320                    taskstatus("1 file copied");
7321                    }
7322                return true;
7323                }
7324            }
7325         return true;
7326         }
7329     virtual bool parse(Element *elem)
7330         {
7331         if (!parent.getAttribute(elem, "file", fileNameOpt))
7332             return false;
7333         if (!parent.getAttribute(elem, "tofile", toFileNameOpt))
7334             return false;
7335         if (toFileNameOpt.size() > 0)
7336             cptype = CP_TOFILE;
7337         if (!parent.getAttribute(elem, "todir", toDirNameOpt))
7338             return false;
7339         if (toDirNameOpt.size() > 0)
7340             cptype = CP_TODIR;
7341         if (!parent.getAttribute(elem, "verbose", verboseOpt))
7342             return false;
7343             
7344         haveFileSet = false;
7345         
7346         std::vector<Element *> children = elem->getChildren();
7347         for (unsigned int i=0 ; i<children.size() ; i++)
7348             {
7349             Element *child = children[i];
7350             String tagName = child->getName();
7351             if (tagName == "fileset")
7352                 {
7353                 if (!parseFileSet(child, parent, fileSet))
7354                     {
7355                     error("problem getting fileset");
7356                     return false;
7357                     }
7358                 haveFileSet = true;
7359                 }
7360             }
7362         //Perform validity checks
7363         if (fileNameOpt.size()>0 && fileSet.size()>0)
7364             {
7365             error("<copy> can only have one of : file= and <fileset>");
7366             return false;
7367             }
7368         if (toFileNameOpt.size()>0 && toDirNameOpt.size()>0)
7369             {
7370             error("<copy> can only have one of : tofile= or todir=");
7371             return false;
7372             }
7373         if (haveFileSet && toDirNameOpt.size()==0)
7374             {
7375             error("a <copy> task with a <fileset> must have : todir=");
7376             return false;
7377             }
7378         if (cptype == CP_TOFILE && fileNameOpt.size()==0)
7379             {
7380             error("<copy> tofile= must be associated with : file=");
7381             return false;
7382             }
7383         if (cptype == CP_TODIR && fileNameOpt.size()==0 && !haveFileSet)
7384             {
7385             error("<copy> todir= must be associated with : file= or <fileset>");
7386             return false;
7387             }
7389         return true;
7390         }
7391         
7392 private:
7394     int cptype;
7395     bool haveFileSet;
7397     FileSet fileSet;
7398     String  fileNameOpt;
7399     String  toFileNameOpt;
7400     String  toDirNameOpt;
7401     String  verboseOpt;
7402 };
7405 /**
7406  * Generate CxxTest files
7407  */
7408 class TaskCxxTestPart: public Task
7410 public:
7412     TaskCxxTestPart(MakeBase &par) : Task(par)
7413          {
7414          type    = TASK_CXXTEST_PART;
7415          name    = "cxxtestpart";
7416          }
7418     virtual ~TaskCxxTestPart()
7419         {}
7421     virtual bool execute()
7422         {
7423         if (!listFiles(parent, fileSet))
7424             return false;
7425         String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
7426                 
7427         String fullDest = parent.resolve(parent.eval(destPathOpt, "."));
7428         String cmd = parent.eval(commandOpt, "cxxtestgen.py");
7429         cmd.append(" --part -o ");
7430         cmd.append(fullDest);
7432         unsigned int newFiles = 0;
7433         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7434             {
7435             String fileName = fileSet[i];
7436             if (getSuffix(fileName) != "h")
7437                 continue;
7438             String sourcePath;
7439             if (fileSetDir.size()>0)
7440                 {
7441                 sourcePath.append(fileSetDir);
7442                 sourcePath.append("/");
7443                 }
7444             sourcePath.append(fileName);
7445             String fullSource = parent.resolve(sourcePath);
7447             cmd.append(" ");
7448             cmd.append(fullSource);
7449             if (isNewerThan(fullSource, fullDest)) newFiles++;
7450             }
7451         
7452         if (newFiles>0) {
7453             size_t const lastSlash = fullDest.find_last_of('/');
7454             if (lastSlash != fullDest.npos) {
7455                 String directory(fullDest, 0, lastSlash);
7456                 if (!createDirectory(directory))
7457                     return false;
7458             }
7460             String outString, errString;
7461             if (!executeCommand(cmd.c_str(), "", outString, errString))
7462                 {
7463                 error("<cxxtestpart> problem: %s", errString.c_str());
7464                 return false;
7465                 }
7466             removeFromStatCache(getNativePath(fullDest));
7467         }
7469         return true;
7470         }
7472     virtual bool parse(Element *elem)
7473         {
7474         if (!parent.getAttribute(elem, "command", commandOpt))
7475             return false;
7476         if (!parent.getAttribute(elem, "out", destPathOpt))
7477             return false;
7478             
7479         std::vector<Element *> children = elem->getChildren();
7480         for (unsigned int i=0 ; i<children.size() ; i++)
7481             {
7482             Element *child = children[i];
7483             String tagName = child->getName();
7484             if (tagName == "fileset")
7485                 {
7486                 if (!parseFileSet(child, parent, fileSet))
7487                     return false;
7488                 }
7489             }
7490         return true;
7491         }
7493 private:
7495     String  commandOpt;
7496     String  destPathOpt;
7497     FileSet fileSet;
7499 };
7502 /**
7503  * Generate the CxxTest root file
7504  */
7505 class TaskCxxTestRoot: public Task
7507 public:
7509     TaskCxxTestRoot(MakeBase &par) : Task(par)
7510          {
7511          type    = TASK_CXXTEST_ROOT;
7512          name    = "cxxtestroot";
7513          }
7515     virtual ~TaskCxxTestRoot()
7516         {}
7518     virtual bool execute()
7519         {
7520         if (!listFiles(parent, fileSet))
7521             return false;
7522         String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
7523         unsigned int newFiles = 0;
7524                 
7525         String fullDest = parent.resolve(parent.eval(destPathOpt, "."));
7526         String cmd = parent.eval(commandOpt, "cxxtestgen.py");
7527         cmd.append(" --root -o ");
7528         cmd.append(fullDest);
7529         String templateFile = parent.eval(templateFileOpt, "");
7530         if (templateFile.size()>0) {
7531             String fullTemplate = parent.resolve(templateFile);
7532             cmd.append(" --template=");
7533             cmd.append(fullTemplate);
7534             if (isNewerThan(fullTemplate, fullDest)) newFiles++;
7535         }
7537         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7538             {
7539             String fileName = fileSet[i];
7540             if (getSuffix(fileName) != "h")
7541                 continue;
7542             String sourcePath;
7543             if (fileSetDir.size()>0)
7544                 {
7545                 sourcePath.append(fileSetDir);
7546                 sourcePath.append("/");
7547                 }
7548             sourcePath.append(fileName);
7549             String fullSource = parent.resolve(sourcePath);
7551             cmd.append(" ");
7552             cmd.append(fullSource);
7553             if (isNewerThan(fullSource, fullDest)) newFiles++;
7554             }
7555         
7556         if (newFiles>0) {
7557             size_t const lastSlash = fullDest.find_last_of('/');
7558             if (lastSlash != fullDest.npos) {
7559                 String directory(fullDest, 0, lastSlash);
7560                 if (!createDirectory(directory))
7561                     return false;
7562             }
7564             String outString, errString;
7565             if (!executeCommand(cmd.c_str(), "", outString, errString))
7566                 {
7567                 error("<cxxtestroot> problem: %s", errString.c_str());
7568                 return false;
7569                 }
7570             removeFromStatCache(getNativePath(fullDest));
7571         }
7573         return true;
7574         }
7576     virtual bool parse(Element *elem)
7577         {
7578         if (!parent.getAttribute(elem, "command", commandOpt))
7579             return false;
7580         if (!parent.getAttribute(elem, "template", templateFileOpt))
7581             return false;
7582         if (!parent.getAttribute(elem, "out", destPathOpt))
7583             return false;
7584             
7585         std::vector<Element *> children = elem->getChildren();
7586         for (unsigned int i=0 ; i<children.size() ; i++)
7587             {
7588             Element *child = children[i];
7589             String tagName = child->getName();
7590             if (tagName == "fileset")
7591                 {
7592                 if (!parseFileSet(child, parent, fileSet))
7593                     return false;
7594                 }
7595             }
7596         return true;
7597         }
7599 private:
7601     String  commandOpt;
7602     String  templateFileOpt;
7603     String  destPathOpt;
7604     FileSet fileSet;
7606 };
7609 /**
7610  * Execute the CxxTest test executable
7611  */
7612 class TaskCxxTestRun: public Task
7614 public:
7616     TaskCxxTestRun(MakeBase &par) : Task(par)
7617          {
7618          type    = TASK_CXXTEST_RUN;
7619          name    = "cxxtestrun";
7620          }
7622     virtual ~TaskCxxTestRun()
7623         {}
7625     virtual bool execute()
7626         {
7627         unsigned int newFiles = 0;
7628                 
7629         String workingDir = parent.resolve(parent.eval(workingDirOpt, "inkscape"));
7630         String rawCmd = parent.eval(commandOpt, "build/cxxtests");
7632         String cmdExe;
7633         if (fileExists(rawCmd)) {
7634             cmdExe = rawCmd;
7635         } else if (fileExists(rawCmd + ".exe")) {
7636             cmdExe = rawCmd + ".exe";
7637         } else {
7638             error("<cxxtestrun> problem: cxxtests executable not found! (command=\"%s\")", rawCmd.c_str());
7639         }
7640         // Note that the log file names are based on the exact name used to call cxxtests (it uses argv[0] + ".log"/".xml")
7641         if (isNewerThan(cmdExe, rawCmd + ".log") || isNewerThan(cmdExe, rawCmd + ".xml")) newFiles++;
7643         // Prepend the necessary ../'s
7644         String cmd = rawCmd;
7645         unsigned int workingDirDepth = 0;
7646         bool wasSlash = true;
7647         for(size_t i=0; i<workingDir.size(); i++) {
7648             // This assumes no . and .. parts
7649             if (wasSlash && workingDir[i]!='/') workingDirDepth++;
7650             wasSlash = workingDir[i] == '/';
7651         }
7652         for(size_t i=0; i<workingDirDepth; i++) {
7653             cmd = "../" + cmd;
7654         }
7655         
7656         if (newFiles>0) {
7657             char olddir[1024];
7658             if (workingDir.size()>0) {
7659                 // TODO: Double-check usage of getcwd and handle chdir errors
7660                 getcwd(olddir, 1024);
7661                 chdir(workingDir.c_str());
7662             }
7664             String outString;
7665             if (!executeCommand(cmd.c_str(), "", outString, outString))
7666                 {
7667                 error("<cxxtestrun> problem: %s", outString.c_str());
7668                 return false;
7669                 }
7671             if (workingDir.size()>0) {
7672                 // TODO: Handle errors?
7673                 chdir(olddir);
7674             }
7676             removeFromStatCache(getNativePath(cmd + ".log"));
7677             removeFromStatCache(getNativePath(cmd + ".xml"));
7678         }
7680         return true;
7681         }
7683     virtual bool parse(Element *elem)
7684         {
7685         if (!parent.getAttribute(elem, "command", commandOpt))
7686             return false;
7687         if (!parent.getAttribute(elem, "workingdir", workingDirOpt))
7688             return false;
7689         return true;
7690         }
7692 private:
7694     String  commandOpt;
7695     String  workingDirOpt;
7697 };
7700 /**
7701  *
7702  */
7703 class TaskDelete : public Task
7705 public:
7707     typedef enum
7708         {
7709         DEL_FILE,
7710         DEL_DIR,
7711         DEL_FILESET
7712         } DeleteType;
7714     TaskDelete(MakeBase &par) : Task(par)
7715         { 
7716         type        = TASK_DELETE;
7717         name        = "delete";
7718         delType     = DEL_FILE;
7719         }
7721     virtual ~TaskDelete()
7722         {}
7724     virtual bool execute()
7725         {
7726         String dirName   = parent.eval(dirNameOpt, ".");
7727         String fileName  = parent.eval(fileNameOpt, ".");
7728         bool verbose     = parent.evalBool(verboseOpt, false);
7729         bool quiet       = parent.evalBool(quietOpt, false);
7730         bool failOnError = parent.evalBool(failOnErrorOpt, true);
7731         switch (delType)
7732             {
7733             case DEL_FILE:
7734                 {
7735                 taskstatus("file: %s", fileName.c_str());
7736                 String fullName = parent.resolve(fileName);
7737                 char *fname = (char *)fullName.c_str();
7738                 if (!quiet && verbose)
7739                     taskstatus("path: %s", fname);
7740                 if (failOnError && !removeFile(fullName))
7741                     {
7742                     //error("Could not delete file '%s'", fullName.c_str());
7743                     return false;
7744                     }
7745                 return true;
7746                 }
7747             case DEL_DIR:
7748                 {
7749                 taskstatus("dir: %s", dirName.c_str());
7750                 String fullDir = parent.resolve(dirName);
7751                 if (!quiet && verbose)
7752                     taskstatus("path: %s", fullDir.c_str());
7753                 if (failOnError && !removeDirectory(fullDir))
7754                     {
7755                     //error("Could not delete directory '%s'", fullDir.c_str());
7756                     return false;
7757                     }
7758                 return true;
7759                 }
7760             }
7761         return true;
7762         }
7764     virtual bool parse(Element *elem)
7765         {
7766         if (!parent.getAttribute(elem, "file", fileNameOpt))
7767             return false;
7768         if (fileNameOpt.size() > 0)
7769             delType = DEL_FILE;
7770         if (!parent.getAttribute(elem, "dir", dirNameOpt))
7771             return false;
7772         if (dirNameOpt.size() > 0)
7773             delType = DEL_DIR;
7774         if (fileNameOpt.size()>0 && dirNameOpt.size()>0)
7775             {
7776             error("<delete> can have one attribute of file= or dir=");
7777             return false;
7778             }
7779         if (fileNameOpt.size()==0 && dirNameOpt.size()==0)
7780             {
7781             error("<delete> must have one attribute of file= or dir=");
7782             return false;
7783             }
7784         if (!parent.getAttribute(elem, "verbose", verboseOpt))
7785             return false;
7786         if (!parent.getAttribute(elem, "quiet", quietOpt))
7787             return false;
7788         if (!parent.getAttribute(elem, "failonerror", failOnErrorOpt))
7789             return false;
7790         return true;
7791         }
7793 private:
7795     int delType;
7796     String dirNameOpt;
7797     String fileNameOpt;
7798     String verboseOpt;
7799     String quietOpt;
7800     String failOnErrorOpt;
7801 };
7804 /**
7805  * Send a message to stdout
7806  */
7807 class TaskEcho : public Task
7809 public:
7811     TaskEcho(MakeBase &par) : Task(par)
7812         { type = TASK_ECHO; name = "echo"; }
7814     virtual ~TaskEcho()
7815         {}
7817     virtual bool execute()
7818         {
7819         //let message have priority over text
7820         String message = parent.eval(messageOpt, "");
7821         String text    = parent.eval(textOpt, "");
7822         if (message.size() > 0)
7823             {
7824             fprintf(stdout, "%s\n", message.c_str());
7825             }
7826         else if (text.size() > 0)
7827             {
7828             fprintf(stdout, "%s\n", text.c_str());
7829             }
7830         return true;
7831         }
7833     virtual bool parse(Element *elem)
7834         {
7835         if (!parent.getValue(elem, textOpt))
7836             return false;
7837         textOpt    = leftJustify(textOpt);
7838         if (!parent.getAttribute(elem, "message", messageOpt))
7839             return false;
7840         return true;
7841         }
7843 private:
7845     String messageOpt;
7846     String textOpt;
7847 };
7851 /**
7852  *
7853  */
7854 class TaskJar : public Task
7856 public:
7858     TaskJar(MakeBase &par) : Task(par)
7859         { type = TASK_JAR; name = "jar"; }
7861     virtual ~TaskJar()
7862         {}
7864     virtual bool execute()
7865         {
7866         String command  = parent.eval(commandOpt, "jar");
7867         String basedir  = parent.eval(basedirOpt, ".");
7868         String destfile = parent.eval(destfileOpt, ".");
7870         String cmd = command;
7871         cmd.append(" -cf ");
7872         cmd.append(destfile);
7873         cmd.append(" -C ");
7874         cmd.append(basedir);
7875         cmd.append(" .");
7877         String execCmd = cmd;
7879         String outString, errString;
7880         bool ret = executeCommand(execCmd.c_str(), "", outString, errString);
7881         if (!ret)
7882             {
7883             error("<jar> command '%s' failed :\n %s",
7884                                       execCmd.c_str(), errString.c_str());
7885             return false;
7886             }
7887         removeFromStatCache(getNativePath(destfile));
7888         return true;
7889         }
7891     virtual bool parse(Element *elem)
7892         {
7893         if (!parent.getAttribute(elem, "command", commandOpt))
7894             return false;
7895         if (!parent.getAttribute(elem, "basedir", basedirOpt))
7896             return false;
7897         if (!parent.getAttribute(elem, "destfile", destfileOpt))
7898             return false;
7899         if (basedirOpt.size() == 0 || destfileOpt.size() == 0)
7900             {
7901             error("<jar> required both basedir and destfile attributes to be set");
7902             return false;
7903             }
7904         return true;
7905         }
7907 private:
7909     String commandOpt;
7910     String basedirOpt;
7911     String destfileOpt;
7912 };
7915 /**
7916  *
7917  */
7918 class TaskJavac : public Task
7920 public:
7922     TaskJavac(MakeBase &par) : Task(par)
7923         { 
7924         type = TASK_JAVAC; name = "javac";
7925         }
7927     virtual ~TaskJavac()
7928         {}
7930     virtual bool execute()
7931         {
7932         String command  = parent.eval(commandOpt, "javac");
7933         String srcdir   = parent.eval(srcdirOpt, ".");
7934         String destdir  = parent.eval(destdirOpt, ".");
7935         String target   = parent.eval(targetOpt, "");
7937         std::vector<String> fileList;
7938         if (!listFiles(srcdir, "", fileList))
7939             {
7940             return false;
7941             }
7942         String cmd = command;
7943         cmd.append(" -d ");
7944         cmd.append(destdir);
7945         cmd.append(" -classpath ");
7946         cmd.append(destdir);
7947         cmd.append(" -sourcepath ");
7948         cmd.append(srcdir);
7949         cmd.append(" ");
7950         if (target.size()>0)
7951             {
7952             cmd.append(" -target ");
7953             cmd.append(target);
7954             cmd.append(" ");
7955             }
7956         String fname = "javalist.btool";
7957         FILE *f = fopen(fname.c_str(), "w");
7958         int count = 0;
7959         for (unsigned int i=0 ; i<fileList.size() ; i++)
7960             {
7961             String fname = fileList[i];
7962             String srcName = fname;
7963             if (fname.size()<6) //x.java
7964                 continue;
7965             if (fname.compare(fname.size()-5, 5, ".java") != 0)
7966                 continue;
7967             String baseName = fname.substr(0, fname.size()-5);
7968             String destName = baseName;
7969             destName.append(".class");
7971             String fullSrc = srcdir;
7972             fullSrc.append("/");
7973             fullSrc.append(fname);
7974             String fullDest = destdir;
7975             fullDest.append("/");
7976             fullDest.append(destName);
7977             //trace("fullsrc:%s fulldest:%s", fullSrc.c_str(), fullDest.c_str());
7978             if (!isNewerThan(fullSrc, fullDest))
7979                 continue;
7981             count++;
7982             fprintf(f, "%s\n", fullSrc.c_str());
7983             }
7984         fclose(f);
7985         if (!count)
7986             {
7987             taskstatus("nothing to do");
7988             return true;
7989             }
7991         taskstatus("compiling %d files", count);
7993         String execCmd = cmd;
7994         execCmd.append("@");
7995         execCmd.append(fname);
7997         String outString, errString;
7998         bool ret = executeCommand(execCmd.c_str(), "", outString, errString);
7999         if (!ret)
8000             {
8001             error("<javac> command '%s' failed :\n %s",
8002                                       execCmd.c_str(), errString.c_str());
8003             return false;
8004             }
8005         // TODO: 
8006         //removeFromStatCache(getNativePath(........));
8007         return true;
8008         }
8010     virtual bool parse(Element *elem)
8011         {
8012         if (!parent.getAttribute(elem, "command", commandOpt))
8013             return false;
8014         if (!parent.getAttribute(elem, "srcdir", srcdirOpt))
8015             return false;
8016         if (!parent.getAttribute(elem, "destdir", destdirOpt))
8017             return false;
8018         if (srcdirOpt.size() == 0 || destdirOpt.size() == 0)
8019             {
8020             error("<javac> required both srcdir and destdir attributes to be set");
8021             return false;
8022             }
8023         if (!parent.getAttribute(elem, "target", targetOpt))
8024             return false;
8025         return true;
8026         }
8028 private:
8030     String commandOpt;
8031     String srcdirOpt;
8032     String destdirOpt;
8033     String targetOpt;
8035 };
8038 /**
8039  *
8040  */
8041 class TaskLink : public Task
8043 public:
8045     TaskLink(MakeBase &par) : Task(par)
8046         {
8047         type = TASK_LINK; name = "link";
8048         }
8050     virtual ~TaskLink()
8051         {}
8053     virtual bool execute()
8054         {
8055         String  command        = parent.eval(commandOpt, "g++");
8056         String  fileName       = parent.eval(fileNameOpt, "");
8057         String  flags          = parent.eval(flagsOpt, "");
8058         String  libs           = parent.eval(libsOpt, "");
8059         bool    doStrip        = parent.evalBool(doStripOpt, false);
8060         String  symFileName    = parent.eval(symFileNameOpt, "");
8061         String  stripCommand   = parent.eval(stripCommandOpt, "strip");
8062         String  objcopyCommand = parent.eval(objcopyCommandOpt, "objcopy");
8064         if (!listFiles(parent, fileSet))
8065             return false;
8066         String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
8067         //trace("%d files in %s", fileSet.size(), fileSetDir.c_str());
8068         bool doit = false;
8069         String fullTarget = parent.resolve(fileName);
8070         String cmd = command;
8071         cmd.append(" -o ");
8072         cmd.append(fullTarget);
8073         cmd.append(" ");
8074         cmd.append(flags);
8075         for (unsigned int i=0 ; i<fileSet.size() ; i++)
8076             {
8077             cmd.append(" ");
8078             String obj;
8079             if (fileSetDir.size()>0)
8080                 {
8081                 obj.append(fileSetDir);
8082                 obj.append("/");
8083                 }
8084             obj.append(fileSet[i]);
8085             String fullObj = parent.resolve(obj);
8086             String nativeFullObj = getNativePath(fullObj);
8087             cmd.append(nativeFullObj);
8088             //trace("link: tgt:%s obj:%s", fullTarget.c_str(),
8089             //          fullObj.c_str());
8090             if (isNewerThan(fullObj, fullTarget))
8091                 doit = true;
8092             }
8093         cmd.append(" ");
8094         cmd.append(libs);
8095         if (!doit)
8096             {
8097             //trace("link not needed");
8098             return true;
8099             }
8100         //trace("LINK cmd:%s", cmd.c_str());
8103         String outbuf, errbuf;
8104         if (!executeCommand(cmd.c_str(), "", outbuf, errbuf))
8105             {
8106             error("LINK problem: %s", errbuf.c_str());
8107             return false;
8108             }
8109         removeFromStatCache(getNativePath(fullTarget));
8111         if (symFileName.size()>0)
8112             {
8113             String symFullName = parent.resolve(symFileName);
8114             cmd = objcopyCommand;
8115             cmd.append(" --only-keep-debug ");
8116             cmd.append(getNativePath(fullTarget));
8117             cmd.append(" ");
8118             cmd.append(getNativePath(symFullName));
8119             if (!executeCommand(cmd, "", outbuf, errbuf))
8120                 {
8121                 error("<strip> symbol file failed : %s", errbuf.c_str());
8122                 return false;
8123                 }
8124             removeFromStatCache(getNativePath(symFullName));
8125             }
8126             
8127         if (doStrip)
8128             {
8129             cmd = stripCommand;
8130             cmd.append(" ");
8131             cmd.append(getNativePath(fullTarget));
8132             if (!executeCommand(cmd, "", outbuf, errbuf))
8133                {
8134                error("<strip> failed : %s", errbuf.c_str());
8135                return false;
8136                }
8137             removeFromStatCache(getNativePath(fullTarget));
8138             }
8140         return true;
8141         }
8143     virtual bool parse(Element *elem)
8144         {
8145         if (!parent.getAttribute(elem, "command", commandOpt))
8146             return false;
8147         if (!parent.getAttribute(elem, "objcopycommand", objcopyCommandOpt))
8148             return false;
8149         if (!parent.getAttribute(elem, "stripcommand", stripCommandOpt))
8150             return false;
8151         if (!parent.getAttribute(elem, "out", fileNameOpt))
8152             return false;
8153         if (!parent.getAttribute(elem, "strip", doStripOpt))
8154             return false;
8155         if (!parent.getAttribute(elem, "symfile", symFileNameOpt))
8156             return false;
8157             
8158         std::vector<Element *> children = elem->getChildren();
8159         for (unsigned int i=0 ; i<children.size() ; i++)
8160             {
8161             Element *child = children[i];
8162             String tagName = child->getName();
8163             if (tagName == "fileset")
8164                 {
8165                 if (!parseFileSet(child, parent, fileSet))
8166                     return false;
8167                 }
8168             else if (tagName == "flags")
8169                 {
8170                 if (!parent.getValue(child, flagsOpt))
8171                     return false;
8172                 flagsOpt = strip(flagsOpt);
8173                 }
8174             else if (tagName == "libs")
8175                 {
8176                 if (!parent.getValue(child, libsOpt))
8177                     return false;
8178                 libsOpt = strip(libsOpt);
8179                 }
8180             }
8181         return true;
8182         }
8184 private:
8186     FileSet fileSet;
8188     String  commandOpt;
8189     String  fileNameOpt;
8190     String  flagsOpt;
8191     String  libsOpt;
8192     String  doStripOpt;
8193     String  symFileNameOpt;
8194     String  stripCommandOpt;
8195     String  objcopyCommandOpt;
8197 };
8201 /**
8202  * Create a named file
8203  */
8204 class TaskMakeFile : public Task
8206 public:
8208     TaskMakeFile(MakeBase &par) : Task(par)
8209         { type = TASK_MAKEFILE; name = "makefile"; }
8211     virtual ~TaskMakeFile()
8212         {}
8214     virtual bool execute()
8215         {
8216         String fileName = parent.eval(fileNameOpt, "");
8217         bool force      = parent.evalBool(forceOpt, false);
8218         String text     = parent.eval(textOpt, "");
8220         taskstatus("%s", fileName.c_str());
8221         String fullName = parent.resolve(fileName);
8222         if (!force && !isNewerThan(parent.getURI().getPath(), fullName))
8223             {
8224             taskstatus("skipped");
8225             return true;
8226             }
8227         String fullNative = getNativePath(fullName);
8228         //trace("fullName:%s", fullName.c_str());
8229         FILE *f = fopen(fullNative.c_str(), "w");
8230         if (!f)
8231             {
8232             error("<makefile> could not open %s for writing : %s",
8233                 fullName.c_str(), strerror(errno));
8234             return false;
8235             }
8236         for (unsigned int i=0 ; i<text.size() ; i++)
8237             fputc(text[i], f);
8238         fputc('\n', f);
8239         fclose(f);
8240         removeFromStatCache(fullNative);
8241         return true;
8242         }
8244     virtual bool parse(Element *elem)
8245         {
8246         if (!parent.getAttribute(elem, "file", fileNameOpt))
8247             return false;
8248         if (!parent.getAttribute(elem, "force", forceOpt))
8249             return false;
8250         if (fileNameOpt.size() == 0)
8251             {
8252             error("<makefile> requires 'file=\"filename\"' attribute");
8253             return false;
8254             }
8255         if (!parent.getValue(elem, textOpt))
8256             return false;
8257         textOpt = leftJustify(textOpt);
8258         //trace("dirname:%s", dirName.c_str());
8259         return true;
8260         }
8262 private:
8264     String fileNameOpt;
8265     String forceOpt;
8266     String textOpt;
8267 };
8271 /**
8272  * Create a named directory
8273  */
8274 class TaskMkDir : public Task
8276 public:
8278     TaskMkDir(MakeBase &par) : Task(par)
8279         { type = TASK_MKDIR; name = "mkdir"; }
8281     virtual ~TaskMkDir()
8282         {}
8284     virtual bool execute()
8285         {
8286         String dirName = parent.eval(dirNameOpt, ".");
8287         
8288         taskstatus("%s", dirName.c_str());
8289         String fullDir = parent.resolve(dirName);
8290         //trace("fullDir:%s", fullDir.c_str());
8291         if (!createDirectory(fullDir))
8292             return false;
8293         return true;
8294         }
8296     virtual bool parse(Element *elem)
8297         {
8298         if (!parent.getAttribute(elem, "dir", dirNameOpt))
8299             return false;
8300         if (dirNameOpt.size() == 0)
8301             {
8302             error("<mkdir> requires 'dir=\"dirname\"' attribute");
8303             return false;
8304             }
8305         return true;
8306         }
8308 private:
8310     String dirNameOpt;
8311 };
8315 /**
8316  * Create a named directory
8317  */
8318 class TaskMsgFmt: public Task
8320 public:
8322     TaskMsgFmt(MakeBase &par) : Task(par)
8323          { type = TASK_MSGFMT;  name = "msgfmt"; }
8325     virtual ~TaskMsgFmt()
8326         {}
8328     virtual bool execute()
8329         {
8330         String  command   = parent.eval(commandOpt, "msgfmt");
8331         String  toDirName = parent.eval(toDirNameOpt, ".");
8332         String  outName   = parent.eval(outNameOpt, "");
8333         bool    owndir    = parent.evalBool(owndirOpt, false);
8335         if (!listFiles(parent, fileSet))
8336             return false;
8337         String fileSetDir = fileSet.getDirectory();
8339         //trace("msgfmt: %d", fileSet.size());
8340         for (unsigned int i=0 ; i<fileSet.size() ; i++)
8341             {
8342             String fileName = fileSet[i];
8343             if (getSuffix(fileName) != "po")
8344                 continue;
8345             String sourcePath;
8346             if (fileSetDir.size()>0)
8347                 {
8348                 sourcePath.append(fileSetDir);
8349                 sourcePath.append("/");
8350                 }
8351             sourcePath.append(fileName);
8352             String fullSource = parent.resolve(sourcePath);
8354             String destPath;
8355             if (toDirName.size()>0)
8356                 {
8357                 destPath.append(toDirName);
8358                 destPath.append("/");
8359                 }
8360             if (owndir)
8361                 {
8362                 String subdir = fileName;
8363                 unsigned int pos = subdir.find_last_of('.');
8364                 if (pos != subdir.npos)
8365                     subdir = subdir.substr(0, pos);
8366                 destPath.append(subdir);
8367                 destPath.append("/");
8368                 }
8369             //Pick the output file name
8370             if (outName.size() > 0)
8371                 {
8372                 destPath.append(outName);
8373                 }
8374             else
8375                 {
8376                 destPath.append(fileName);
8377                 destPath[destPath.size()-2] = 'm';
8378                 }
8380             String fullDest = parent.resolve(destPath);
8382             if (!isNewerThan(fullSource, fullDest))
8383                 {
8384                 //trace("skip %s", fullSource.c_str());
8385                 continue;
8386                 }
8387                 
8388             String cmd = command;
8389             cmd.append(" ");
8390             cmd.append(fullSource);
8391             cmd.append(" -o ");
8392             cmd.append(fullDest);
8393             
8394             int pos = fullDest.find_last_of('/');
8395             if (pos>0)
8396                 {
8397                 String fullDestPath = fullDest.substr(0, pos);
8398                 if (!createDirectory(fullDestPath))
8399                     return false;
8400                 }
8404             String outString, errString;
8405             if (!executeCommand(cmd.c_str(), "", outString, errString))
8406                 {
8407                 error("<msgfmt> problem: %s", errString.c_str());
8408                 return false;
8409                 }
8410             removeFromStatCache(getNativePath(fullDest));
8411             }
8413         return true;
8414         }
8416     virtual bool parse(Element *elem)
8417         {
8418         if (!parent.getAttribute(elem, "command", commandOpt))
8419             return false;
8420         if (!parent.getAttribute(elem, "todir", toDirNameOpt))
8421             return false;
8422         if (!parent.getAttribute(elem, "out", outNameOpt))
8423             return false;
8424         if (!parent.getAttribute(elem, "owndir", owndirOpt))
8425             return false;
8426             
8427         std::vector<Element *> children = elem->getChildren();
8428         for (unsigned int i=0 ; i<children.size() ; i++)
8429             {
8430             Element *child = children[i];
8431             String tagName = child->getName();
8432             if (tagName == "fileset")
8433                 {
8434                 if (!parseFileSet(child, parent, fileSet))
8435                     return false;
8436                 }
8437             }
8438         return true;
8439         }
8441 private:
8443     FileSet fileSet;
8445     String  commandOpt;
8446     String  toDirNameOpt;
8447     String  outNameOpt;
8448     String  owndirOpt;
8450 };
8454 /**
8455  *  Perform a Package-Config query similar to pkg-config
8456  */
8457 class TaskPkgConfig : public Task
8459 public:
8461     typedef enum
8462         {
8463         PKG_CONFIG_QUERY_CFLAGS,
8464         PKG_CONFIG_QUERY_LIBS,
8465         PKG_CONFIG_QUERY_ALL
8466         } QueryTypes;
8468     TaskPkgConfig(MakeBase &par) : Task(par)
8469         {
8470         type = TASK_PKG_CONFIG;
8471         name = "pkg-config";
8472         }
8474     virtual ~TaskPkgConfig()
8475         {}
8477     virtual bool execute()
8478         {
8479         String pkgName       = parent.eval(pkgNameOpt,      "");
8480         String prefix        = parent.eval(prefixOpt,       "");
8481         String propName      = parent.eval(propNameOpt,     "");
8482         String pkgConfigPath = parent.eval(pkgConfigPathOpt,"");
8483         String query         = parent.eval(queryOpt,        "all");
8485         String path = parent.resolve(pkgConfigPath);
8486         PkgConfig pkgconfig;
8487         pkgconfig.setPath(path);
8488         pkgconfig.setPrefix(prefix);
8489         if (!pkgconfig.query(pkgName))
8490             {
8491             error("<pkg-config> query failed for '%s", name.c_str());
8492             return false;
8493             }
8494             
8495         String val = "";
8496         if (query == "cflags")
8497             val = pkgconfig.getCflags();
8498         else if (query == "libs")
8499             val =pkgconfig.getLibs();
8500         else if (query == "all")
8501             val = pkgconfig.getAll();
8502         else
8503             {
8504             error("<pkg-config> unhandled query : %s", query.c_str());
8505             return false;
8506             }
8507         taskstatus("property %s = '%s'", propName.c_str(), val.c_str());
8508         parent.setProperty(propName, val);
8509         return true;
8510         }
8512     virtual bool parse(Element *elem)
8513         {
8514         //# NAME
8515         if (!parent.getAttribute(elem, "name", pkgNameOpt))
8516             return false;
8517         if (pkgNameOpt.size()==0)
8518             {
8519             error("<pkg-config> requires 'name=\"package\"' attribute");
8520             return false;
8521             }
8523         //# PROPERTY
8524         if (!parent.getAttribute(elem, "property", propNameOpt))
8525             return false;
8526         if (propNameOpt.size()==0)
8527             {
8528             error("<pkg-config> requires 'property=\"name\"' attribute");
8529             return false;
8530             }
8531         //# PATH
8532         if (!parent.getAttribute(elem, "path", pkgConfigPathOpt))
8533             return false;
8534         //# PREFIX
8535         if (!parent.getAttribute(elem, "prefix", prefixOpt))
8536             return false;
8537         //# QUERY
8538         if (!parent.getAttribute(elem, "query", queryOpt))
8539             return false;
8541         return true;
8542         }
8544 private:
8546     String queryOpt;
8547     String pkgNameOpt;
8548     String prefixOpt;
8549     String propNameOpt;
8550     String pkgConfigPathOpt;
8552 };
8559 /**
8560  *  Process an archive to allow random access
8561  */
8562 class TaskRanlib : public Task
8564 public:
8566     TaskRanlib(MakeBase &par) : Task(par)
8567         { type = TASK_RANLIB; name = "ranlib"; }
8569     virtual ~TaskRanlib()
8570         {}
8572     virtual bool execute()
8573         {
8574         String fileName = parent.eval(fileNameOpt, "");
8575         String command  = parent.eval(commandOpt, "ranlib");
8577         String fullName = parent.resolve(fileName);
8578         //trace("fullDir:%s", fullDir.c_str());
8579         String cmd = command;
8580         cmd.append(" ");
8581         cmd.append(fullName);
8582         String outbuf, errbuf;
8583         if (!executeCommand(cmd, "", outbuf, errbuf))
8584             return false;
8585         // TODO:
8586         //removeFromStatCache(getNativePath(fullDest));
8587         return true;
8588         }
8590     virtual bool parse(Element *elem)
8591         {
8592         if (!parent.getAttribute(elem, "command", commandOpt))
8593             return false;
8594         if (!parent.getAttribute(elem, "file", fileNameOpt))
8595             return false;
8596         if (fileNameOpt.size() == 0)
8597             {
8598             error("<ranlib> requires 'file=\"fileNname\"' attribute");
8599             return false;
8600             }
8601         return true;
8602         }
8604 private:
8606     String fileNameOpt;
8607     String commandOpt;
8608 };
8612 /**
8613  * Compile a resource file into a binary object
8614  */
8615 class TaskRC : public Task
8617 public:
8619     TaskRC(MakeBase &par) : Task(par)
8620         { type = TASK_RC; name = "rc"; }
8622     virtual ~TaskRC()
8623         {}
8625     virtual bool execute()
8626         {
8627         String command  = parent.eval(commandOpt,  "windres");
8628         String flags    = parent.eval(flagsOpt,    "");
8629         String fileName = parent.eval(fileNameOpt, "");
8630         String outName  = parent.eval(outNameOpt,  "");
8632         String fullFile = parent.resolve(fileName);
8633         String fullOut  = parent.resolve(outName);
8634         if (!isNewerThan(fullFile, fullOut))
8635             return true;
8636         String cmd = command;
8637         cmd.append(" -o ");
8638         cmd.append(fullOut);
8639         cmd.append(" ");
8640         cmd.append(flags);
8641         cmd.append(" ");
8642         cmd.append(fullFile);
8644         String outString, errString;
8645         if (!executeCommand(cmd.c_str(), "", outString, errString))
8646             {
8647             error("RC problem: %s", errString.c_str());
8648             return false;
8649             }
8650         removeFromStatCache(getNativePath(fullOut));
8651         return true;
8652         }
8654     virtual bool parse(Element *elem)
8655         {
8656         if (!parent.getAttribute(elem, "command", commandOpt))
8657             return false;
8658         if (!parent.getAttribute(elem, "file", fileNameOpt))
8659             return false;
8660         if (!parent.getAttribute(elem, "out", outNameOpt))
8661             return false;
8662         std::vector<Element *> children = elem->getChildren();
8663         for (unsigned int i=0 ; i<children.size() ; i++)
8664             {
8665             Element *child = children[i];
8666             String tagName = child->getName();
8667             if (tagName == "flags")
8668                 {
8669                 if (!parent.getValue(child, flagsOpt))
8670                     return false;
8671                 }
8672             }
8673         return true;
8674         }
8676 private:
8678     String commandOpt;
8679     String flagsOpt;
8680     String fileNameOpt;
8681     String outNameOpt;
8683 };
8687 /**
8688  *  Collect .o's into a .so or DLL
8689  */
8690 class TaskSharedLib : public Task
8692 public:
8694     TaskSharedLib(MakeBase &par) : Task(par)
8695         { type = TASK_SHAREDLIB; name = "dll"; }
8697     virtual ~TaskSharedLib()
8698         {}
8700     virtual bool execute()
8701         {
8702         String command     = parent.eval(commandOpt, "dllwrap");
8703         String fileName    = parent.eval(fileNameOpt, "");
8704         String defFileName = parent.eval(defFileNameOpt, "");
8705         String impFileName = parent.eval(impFileNameOpt, "");
8706         String libs        = parent.eval(libsOpt, "");
8708         //trace("###########HERE %d", fileSet.size());
8709         bool doit = false;
8710         
8711         String fullOut = parent.resolve(fileName);
8712         //trace("ar fullout: %s", fullOut.c_str());
8713         
8714         if (!listFiles(parent, fileSet))
8715             return false;
8716         String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
8718         for (unsigned int i=0 ; i<fileSet.size() ; i++)
8719             {
8720             String fname;
8721             if (fileSetDir.size()>0)
8722                 {
8723                 fname.append(fileSetDir);
8724                 fname.append("/");
8725                 }
8726             fname.append(fileSet[i]);
8727             String fullName = parent.resolve(fname);
8728             //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
8729             if (isNewerThan(fullName, fullOut))
8730                 doit = true;
8731             }
8732         //trace("Needs it:%d", doit);
8733         if (!doit)
8734             {
8735             return true;
8736             }
8738         String cmd = "dllwrap";
8739         cmd.append(" -o ");
8740         cmd.append(fullOut);
8741         if (defFileName.size()>0)
8742             {
8743             cmd.append(" --def ");
8744             cmd.append(defFileName);
8745             cmd.append(" ");
8746             }
8747         if (impFileName.size()>0)
8748             {
8749             cmd.append(" --implib ");
8750             cmd.append(impFileName);
8751             cmd.append(" ");
8752             }
8753         for (unsigned int i=0 ; i<fileSet.size() ; i++)
8754             {
8755             String fname;
8756             if (fileSetDir.size()>0)
8757                 {
8758                 fname.append(fileSetDir);
8759                 fname.append("/");
8760                 }
8761             fname.append(fileSet[i]);
8762             String fullName = parent.resolve(fname);
8764             cmd.append(" ");
8765             cmd.append(fullName);
8766             }
8767         cmd.append(" ");
8768         cmd.append(libs);
8770         String outString, errString;
8771         if (!executeCommand(cmd.c_str(), "", outString, errString))
8772             {
8773             error("<sharedlib> problem: %s", errString.c_str());
8774             return false;
8775             }
8776         removeFromStatCache(getNativePath(fullOut));
8777         return true;
8778         }
8780     virtual bool parse(Element *elem)
8781         {
8782         if (!parent.getAttribute(elem, "command", commandOpt))
8783             return false;
8784         if (!parent.getAttribute(elem, "file", fileNameOpt))
8785             return false;
8786         if (!parent.getAttribute(elem, "import", impFileNameOpt))
8787             return false;
8788         if (!parent.getAttribute(elem, "def", defFileNameOpt))
8789             return false;
8790             
8791         std::vector<Element *> children = elem->getChildren();
8792         for (unsigned int i=0 ; i<children.size() ; i++)
8793             {
8794             Element *child = children[i];
8795             String tagName = child->getName();
8796             if (tagName == "fileset")
8797                 {
8798                 if (!parseFileSet(child, parent, fileSet))
8799                     return false;
8800                 }
8801             else if (tagName == "libs")
8802                 {
8803                 if (!parent.getValue(child, libsOpt))
8804                     return false;
8805                 libsOpt = strip(libsOpt);
8806                 }
8807             }
8808         return true;
8809         }
8811 private:
8813     FileSet fileSet;
8815     String commandOpt;
8816     String fileNameOpt;
8817     String defFileNameOpt;
8818     String impFileNameOpt;
8819     String libsOpt;
8821 };
8825 /**
8826  * Run the "ar" command to archive .o's into a .a
8827  */
8828 class TaskStaticLib : public Task
8830 public:
8832     TaskStaticLib(MakeBase &par) : Task(par)
8833         { type = TASK_STATICLIB; name = "staticlib"; }
8835     virtual ~TaskStaticLib()
8836         {}
8838     virtual bool execute()
8839         {
8840         String command = parent.eval(commandOpt, "ar crv");
8841         String fileName = parent.eval(fileNameOpt, "");
8843         bool doit = false;
8844         
8845         String fullOut = parent.resolve(fileName);
8846         //trace("ar fullout: %s", fullOut.c_str());
8847         
8848         if (!listFiles(parent, fileSet))
8849             return false;
8850         String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
8851         //trace("###########HERE %s", fileSetDir.c_str());
8853         for (unsigned int i=0 ; i<fileSet.size() ; i++)
8854             {
8855             String fname;
8856             if (fileSetDir.size()>0)
8857                 {
8858                 fname.append(fileSetDir);
8859                 fname.append("/");
8860                 }
8861             fname.append(fileSet[i]);
8862             String fullName = parent.resolve(fname);
8863             //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
8864             if (isNewerThan(fullName, fullOut))
8865                 doit = true;
8866             }
8867         //trace("Needs it:%d", doit);
8868         if (!doit)
8869             {
8870             return true;
8871             }
8873         String cmd = command;
8874         cmd.append(" ");
8875         cmd.append(fullOut);
8876         for (unsigned int i=0 ; i<fileSet.size() ; i++)
8877             {
8878             String fname;
8879             if (fileSetDir.size()>0)
8880                 {
8881                 fname.append(fileSetDir);
8882                 fname.append("/");
8883                 }
8884             fname.append(fileSet[i]);
8885             String fullName = parent.resolve(fname);
8887             cmd.append(" ");
8888             cmd.append(fullName);
8889             }
8891         String outString, errString;
8892         if (!executeCommand(cmd.c_str(), "", outString, errString))
8893             {
8894             error("<staticlib> problem: %s", errString.c_str());
8895             return false;
8896             }
8897         removeFromStatCache(getNativePath(fullOut));
8898         return true;
8899         }
8902     virtual bool parse(Element *elem)
8903         {
8904         if (!parent.getAttribute(elem, "command", commandOpt))
8905             return false;
8906         if (!parent.getAttribute(elem, "file", fileNameOpt))
8907             return false;
8908             
8909         std::vector<Element *> children = elem->getChildren();
8910         for (unsigned int i=0 ; i<children.size() ; i++)
8911             {
8912             Element *child = children[i];
8913             String tagName = child->getName();
8914             if (tagName == "fileset")
8915                 {
8916                 if (!parseFileSet(child, parent, fileSet))
8917                     return false;
8918                 }
8919             }
8920         return true;
8921         }
8923 private:
8925     FileSet fileSet;
8927     String commandOpt;
8928     String fileNameOpt;
8930 };
8935 /**
8936  * Strip an executable
8937  */
8938 class TaskStrip : public Task
8940 public:
8942     TaskStrip(MakeBase &par) : Task(par)
8943         { type = TASK_STRIP; name = "strip"; }
8945     virtual ~TaskStrip()
8946         {}
8948     virtual bool execute()
8949         {
8950         String command     = parent.eval(commandOpt, "strip");
8951         String fileName    = parent.eval(fileNameOpt, "");
8952         String symFileName = parent.eval(symFileNameOpt, "");
8954         String fullName = parent.resolve(fileName);
8955         //trace("fullDir:%s", fullDir.c_str());
8956         String cmd;
8957         String outbuf, errbuf;
8959         if (symFileName.size()>0)
8960             {
8961             String symFullName = parent.resolve(symFileName);
8962             cmd = "objcopy --only-keep-debug ";
8963             cmd.append(getNativePath(fullName));
8964             cmd.append(" ");
8965             cmd.append(getNativePath(symFullName));
8966             if (!executeCommand(cmd, "", outbuf, errbuf))
8967                 {
8968                 error("<strip> symbol file failed : %s", errbuf.c_str());
8969                 return false;
8970                 }
8971             }
8972             
8973         cmd = command;
8974         cmd.append(getNativePath(fullName));
8975         if (!executeCommand(cmd, "", outbuf, errbuf))
8976             {
8977             error("<strip> failed : %s", errbuf.c_str());
8978             return false;
8979             }
8980         removeFromStatCache(getNativePath(fullName));
8981         return true;
8982         }
8984     virtual bool parse(Element *elem)
8985         {
8986         if (!parent.getAttribute(elem, "command", commandOpt))
8987             return false;
8988         if (!parent.getAttribute(elem, "file", fileNameOpt))
8989             return false;
8990         if (!parent.getAttribute(elem, "symfile", symFileNameOpt))
8991             return false;
8992         if (fileNameOpt.size() == 0)
8993             {
8994             error("<strip> requires 'file=\"fileName\"' attribute");
8995             return false;
8996             }
8997         return true;
8998         }
9000 private:
9002     String commandOpt;
9003     String fileNameOpt;
9004     String symFileNameOpt;
9005 };
9008 /**
9009  *
9010  */
9011 class TaskTouch : public Task
9013 public:
9015     TaskTouch(MakeBase &par) : Task(par)
9016         { type = TASK_TOUCH; name = "touch"; }
9018     virtual ~TaskTouch()
9019         {}
9021     virtual bool execute()
9022         {
9023         String fileName = parent.eval(fileNameOpt, "");
9025         String fullName = parent.resolve(fileName);
9026         String nativeFile = getNativePath(fullName);
9027         if (!isRegularFile(fullName) && !isDirectory(fullName))
9028             {            
9029             // S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH
9030             int ret = creat(nativeFile.c_str(), 0666);
9031             if (ret != 0) 
9032                 {
9033                 error("<touch> could not create '%s' : %s",
9034                     nativeFile.c_str(), strerror(ret));
9035                 return false;
9036                 }
9037             return true;
9038             }
9039         int ret = utime(nativeFile.c_str(), (struct utimbuf *)0);
9040         if (ret != 0)
9041             {
9042             error("<touch> could not update the modification time for '%s' : %s",
9043                 nativeFile.c_str(), strerror(ret));
9044             return false;
9045             }
9046         removeFromStatCache(nativeFile);
9047         return true;
9048         }
9050     virtual bool parse(Element *elem)
9051         {
9052         //trace("touch parse");
9053         if (!parent.getAttribute(elem, "file", fileNameOpt))
9054             return false;
9055         if (fileNameOpt.size() == 0)
9056             {
9057             error("<touch> requires 'file=\"fileName\"' attribute");
9058             return false;
9059             }
9060         return true;
9061         }
9063     String fileNameOpt;
9064 };
9067 /**
9068  *
9069  */
9070 class TaskTstamp : public Task
9072 public:
9074     TaskTstamp(MakeBase &par) : Task(par)
9075         { type = TASK_TSTAMP; name = "tstamp"; }
9077     virtual ~TaskTstamp()
9078         {}
9080     virtual bool execute()
9081         {
9082         return true;
9083         }
9085     virtual bool parse(Element *elem)
9086         {
9087         //trace("tstamp parse");
9088         return true;
9089         }
9090 };
9094 /**
9095  *
9096  */
9097 Task *Task::createTask(Element *elem, int lineNr)
9099     String tagName = elem->getName();
9100     //trace("task:%s", tagName.c_str());
9101     Task *task = NULL;
9102     if (tagName == "cc")
9103         task = new TaskCC(parent);
9104     else if (tagName == "copy")
9105         task = new TaskCopy(parent);
9106     else if (tagName == "cxxtestpart")
9107         task = new TaskCxxTestPart(parent);
9108     else if (tagName == "cxxtestroot")
9109         task = new TaskCxxTestRoot(parent);
9110     else if (tagName == "cxxtestrun")
9111         task = new TaskCxxTestRun(parent);
9112     else if (tagName == "delete")
9113         task = new TaskDelete(parent);
9114     else if (tagName == "echo")
9115         task = new TaskEcho(parent);
9116     else if (tagName == "jar")
9117         task = new TaskJar(parent);
9118     else if (tagName == "javac")
9119         task = new TaskJavac(parent);
9120     else if (tagName == "link")
9121         task = new TaskLink(parent);
9122     else if (tagName == "makefile")
9123         task = new TaskMakeFile(parent);
9124     else if (tagName == "mkdir")
9125         task = new TaskMkDir(parent);
9126     else if (tagName == "msgfmt")
9127         task = new TaskMsgFmt(parent);
9128     else if (tagName == "pkg-config")
9129         task = new TaskPkgConfig(parent);
9130     else if (tagName == "ranlib")
9131         task = new TaskRanlib(parent);
9132     else if (tagName == "rc")
9133         task = new TaskRC(parent);
9134     else if (tagName == "sharedlib")
9135         task = new TaskSharedLib(parent);
9136     else if (tagName == "staticlib")
9137         task = new TaskStaticLib(parent);
9138     else if (tagName == "strip")
9139         task = new TaskStrip(parent);
9140     else if (tagName == "touch")
9141         task = new TaskTouch(parent);
9142     else if (tagName == "tstamp")
9143         task = new TaskTstamp(parent);
9144     else
9145         {
9146         error("Unknown task '%s'", tagName.c_str());
9147         return NULL;
9148         }
9150     task->setLine(lineNr);
9152     if (!task->parse(elem))
9153         {
9154         delete task;
9155         return NULL;
9156         }
9157     return task;
9162 //########################################################################
9163 //# T A R G E T
9164 //########################################################################
9166 /**
9167  *
9168  */
9169 class Target : public MakeBase
9172 public:
9174     /**
9175      *
9176      */
9177     Target(Make &par) : parent(par)
9178         { init(); }
9180     /**
9181      *
9182      */
9183     Target(const Target &other) : parent(other.parent)
9184         { init(); assign(other); }
9186     /**
9187      *
9188      */
9189     Target &operator=(const Target &other)
9190         { init(); assign(other); return *this; }
9192     /**
9193      *
9194      */
9195     virtual ~Target()
9196         { cleanup() ; }
9199     /**
9200      *
9201      */
9202     virtual Make &getParent()
9203         { return parent; }
9205     /**
9206      *
9207      */
9208     virtual String getName()
9209         { return name; }
9211     /**
9212      *
9213      */
9214     virtual void setName(const String &val)
9215         { name = val; }
9217     /**
9218      *
9219      */
9220     virtual String getDescription()
9221         { return description; }
9223     /**
9224      *
9225      */
9226     virtual void setDescription(const String &val)
9227         { description = val; }
9229     /**
9230      *
9231      */
9232     virtual void addDependency(const String &val)
9233         { deps.push_back(val); }
9235     /**
9236      *
9237      */
9238     virtual void parseDependencies(const String &val)
9239         { deps = tokenize(val, ", "); }
9241     /**
9242      *
9243      */
9244     virtual std::vector<String> &getDependencies()
9245         { return deps; }
9247     /**
9248      *
9249      */
9250     virtual String getIf()
9251         { return ifVar; }
9253     /**
9254      *
9255      */
9256     virtual void setIf(const String &val)
9257         { ifVar = val; }
9259     /**
9260      *
9261      */
9262     virtual String getUnless()
9263         { return unlessVar; }
9265     /**
9266      *
9267      */
9268     virtual void setUnless(const String &val)
9269         { unlessVar = val; }
9271     /**
9272      *
9273      */
9274     virtual void addTask(Task *val)
9275         { tasks.push_back(val); }
9277     /**
9278      *
9279      */
9280     virtual std::vector<Task *> &getTasks()
9281         { return tasks; }
9283 private:
9285     void init()
9286         {
9287         }
9289     void cleanup()
9290         {
9291         tasks.clear();
9292         }
9294     void assign(const Target &other)
9295         {
9296         //parent      = other.parent;
9297         name        = other.name;
9298         description = other.description;
9299         ifVar       = other.ifVar;
9300         unlessVar   = other.unlessVar;
9301         deps        = other.deps;
9302         tasks       = other.tasks;
9303         }
9305     Make &parent;
9307     String name;
9309     String description;
9311     String ifVar;
9313     String unlessVar;
9315     std::vector<String> deps;
9317     std::vector<Task *> tasks;
9319 };
9328 //########################################################################
9329 //# M A K E
9330 //########################################################################
9333 /**
9334  *
9335  */
9336 class Make : public MakeBase
9339 public:
9341     /**
9342      *
9343      */
9344     Make()
9345         { init(); }
9347     /**
9348      *
9349      */
9350     Make(const Make &other)
9351         { assign(other); }
9353     /**
9354      *
9355      */
9356     Make &operator=(const Make &other)
9357         { assign(other); return *this; }
9359     /**
9360      *
9361      */
9362     virtual ~Make()
9363         { cleanup(); }
9365     /**
9366      *
9367      */
9368     virtual std::map<String, Target> &getTargets()
9369         { return targets; }
9372     /**
9373      *
9374      */
9375     virtual String version()
9376         { return BUILDTOOL_VERSION; }
9378     /**
9379      * Overload a <property>
9380      */
9381     virtual bool specifyProperty(const String &name,
9382                                  const String &value);
9384     /**
9385      *
9386      */
9387     virtual bool run();
9389     /**
9390      *
9391      */
9392     virtual bool run(const String &target);
9396 private:
9398     /**
9399      *
9400      */
9401     void init();
9403     /**
9404      *
9405      */
9406     void cleanup();
9408     /**
9409      *
9410      */
9411     void assign(const Make &other);
9413     /**
9414      *
9415      */
9416     bool executeTask(Task &task);
9419     /**
9420      *
9421      */
9422     bool executeTarget(Target &target,
9423              std::set<String> &targetsCompleted);
9426     /**
9427      *
9428      */
9429     bool execute();
9431     /**
9432      *
9433      */
9434     bool checkTargetDependencies(Target &prop,
9435                     std::vector<String> &depList);
9437     /**
9438      *
9439      */
9440     bool parsePropertyFile(const String &fileName,
9441                            const String &prefix);
9443     /**
9444      *
9445      */
9446     bool parseProperty(Element *elem);
9448     /**
9449      *
9450      */
9451     bool parseFile();
9453     /**
9454      *
9455      */
9456     std::vector<String> glob(const String &pattern);
9459     //###############
9460     //# Fields
9461     //###############
9463     String projectName;
9465     String currentTarget;
9467     String defaultTarget;
9469     String specifiedTarget;
9471     String baseDir;
9473     String description;
9474     
9475     //std::vector<Property> properties;
9476     
9477     std::map<String, Target> targets;
9479     std::vector<Task *> allTasks;
9480     
9481     std::map<String, String> specifiedProperties;
9483 };
9486 //########################################################################
9487 //# C L A S S  M A I N T E N A N C E
9488 //########################################################################
9490 /**
9491  *
9492  */
9493 void Make::init()
9495     uri             = "build.xml";
9496     projectName     = "";
9497     currentTarget   = "";
9498     defaultTarget   = "";
9499     specifiedTarget = "";
9500     baseDir         = "";
9501     description     = "";
9502     envPrefix       = "env.";
9503     pcPrefix        = "pc.";
9504     pccPrefix       = "pcc.";
9505     pclPrefix       = "pcl.";
9506     svnPrefix       = "svn.";
9507     properties.clear();
9508     for (unsigned int i = 0 ; i < allTasks.size() ; i++)
9509         delete allTasks[i];
9510     allTasks.clear();
9515 /**
9516  *
9517  */
9518 void Make::cleanup()
9520     for (unsigned int i = 0 ; i < allTasks.size() ; i++)
9521         delete allTasks[i];
9522     allTasks.clear();
9527 /**
9528  *
9529  */
9530 void Make::assign(const Make &other)
9532     uri              = other.uri;
9533     projectName      = other.projectName;
9534     currentTarget    = other.currentTarget;
9535     defaultTarget    = other.defaultTarget;
9536     specifiedTarget  = other.specifiedTarget;
9537     baseDir          = other.baseDir;
9538     description      = other.description;
9539     properties       = other.properties;
9544 //########################################################################
9545 //# U T I L I T Y    T A S K S
9546 //########################################################################
9548 /**
9549  *  Perform a file globbing
9550  */
9551 std::vector<String> Make::glob(const String &pattern)
9553     std::vector<String> res;
9554     return res;
9558 //########################################################################
9559 //# P U B L I C    A P I
9560 //########################################################################
9564 /**
9565  *
9566  */
9567 bool Make::executeTarget(Target &target,
9568              std::set<String> &targetsCompleted)
9571     String name = target.getName();
9573     //First get any dependencies for this target
9574     std::vector<String> deps = target.getDependencies();
9575     for (unsigned int i=0 ; i<deps.size() ; i++)
9576         {
9577         String dep = deps[i];
9578         //Did we do it already?  Skip
9579         if (targetsCompleted.find(dep)!=targetsCompleted.end())
9580             continue;
9581             
9582         std::map<String, Target> &tgts =
9583                target.getParent().getTargets();
9584         std::map<String, Target>::iterator iter =
9585                tgts.find(dep);
9586         if (iter == tgts.end())
9587             {
9588             error("Target '%s' dependency '%s' not found",
9589                       name.c_str(),  dep.c_str());
9590             return false;
9591             }
9592         Target depTarget = iter->second;
9593         if (!executeTarget(depTarget, targetsCompleted))
9594             {
9595             return false;
9596             }
9597         }
9599     status("##### Target : %s\n##### %s", name.c_str(),
9600             target.getDescription().c_str());
9602     //Now let's do the tasks
9603     std::vector<Task *> &tasks = target.getTasks();
9604     for (unsigned int i=0 ; i<tasks.size() ; i++)
9605         {
9606         Task *task = tasks[i];
9607         status("--- %s / %s", name.c_str(), task->getName().c_str());
9608         if (!task->execute())
9609             {
9610             return false;
9611             }
9612         }
9613         
9614     targetsCompleted.insert(name);
9615     
9616     return true;
9621 /**
9622  *  Main execute() method.  Start here and work
9623  *  up the dependency tree 
9624  */
9625 bool Make::execute()
9627     status("######## EXECUTE");
9629     //Determine initial target
9630     if (specifiedTarget.size()>0)
9631         {
9632         currentTarget = specifiedTarget;
9633         }
9634     else if (defaultTarget.size()>0)
9635         {
9636         currentTarget = defaultTarget;
9637         }
9638     else
9639         {
9640         error("execute: no specified or default target requested");
9641         return false;
9642         }
9644     std::map<String, Target>::iterator iter =
9645                targets.find(currentTarget);
9646     if (iter == targets.end())
9647         {
9648         error("Initial target '%s' not found",
9649                  currentTarget.c_str());
9650         return false;
9651         }
9652         
9653     //Now run
9654     Target target = iter->second;
9655     std::set<String> targetsCompleted;
9656     if (!executeTarget(target, targetsCompleted))
9657         {
9658         return false;
9659         }
9661     status("######## EXECUTE COMPLETE");
9662     return true;
9668 /**
9669  *
9670  */
9671 bool Make::checkTargetDependencies(Target &target, 
9672                             std::vector<String> &depList)
9674     String tgtName = target.getName().c_str();
9675     depList.push_back(tgtName);
9677     std::vector<String> deps = target.getDependencies();
9678     for (unsigned int i=0 ; i<deps.size() ; i++)
9679         {
9680         String dep = deps[i];
9681         //First thing entered was the starting Target
9682         if (dep == depList[0])
9683             {
9684             error("Circular dependency '%s' found at '%s'",
9685                       dep.c_str(), tgtName.c_str());
9686             std::vector<String>::iterator diter;
9687             for (diter=depList.begin() ; diter!=depList.end() ; diter++)
9688                 {
9689                 error("  %s", diter->c_str());
9690                 }
9691             return false;
9692             }
9694         std::map<String, Target> &tgts =
9695                   target.getParent().getTargets();
9696         std::map<String, Target>::iterator titer = tgts.find(dep);
9697         if (titer == tgts.end())
9698             {
9699             error("Target '%s' dependency '%s' not found",
9700                       tgtName.c_str(), dep.c_str());
9701             return false;
9702             }
9703         if (!checkTargetDependencies(titer->second, depList))
9704             {
9705             return false;
9706             }
9707         }
9708     return true;
9715 static int getword(int pos, const String &inbuf, String &result)
9717     int p = pos;
9718     int len = (int)inbuf.size();
9719     String val;
9720     while (p < len)
9721         {
9722         char ch = inbuf[p];
9723         if (!isalnum(ch) && ch!='.' && ch!='_')
9724             break;
9725         val.push_back(ch);
9726         p++;
9727         }
9728     result = val;
9729     return p;
9735 /**
9736  *
9737  */
9738 bool Make::parsePropertyFile(const String &fileName,
9739                              const String &prefix)
9741     FILE *f = fopen(fileName.c_str(), "r");
9742     if (!f)
9743         {
9744         error("could not open property file %s", fileName.c_str());
9745         return false;
9746         }
9747     int linenr = 0;
9748     while (!feof(f))
9749         {
9750         char buf[256];
9751         if (!fgets(buf, 255, f))
9752             break;
9753         linenr++;
9754         String s = buf;
9755         s = trim(s);
9756         int len = s.size();
9757         if (len == 0)
9758             continue;
9759         if (s[0] == '#')
9760             continue;
9761         String key;
9762         String val;
9763         int p = 0;
9764         int p2 = getword(p, s, key);
9765         if (p2 <= p)
9766             {
9767             error("property file %s, line %d: expected keyword",
9768                     fileName.c_str(), linenr);
9769             return false;
9770             }
9771         if (prefix.size() > 0)
9772             {
9773             key.insert(0, prefix);
9774             }
9776         //skip whitespace
9777         for (p=p2 ; p<len ; p++)
9778             if (!isspace(s[p]))
9779                 break;
9781         if (p>=len || s[p]!='=')
9782             {
9783             error("property file %s, line %d: expected '='",
9784                     fileName.c_str(), linenr);
9785             return false;
9786             }
9787         p++;
9789         //skip whitespace
9790         for ( ; p<len ; p++)
9791             if (!isspace(s[p]))
9792                 break;
9794         /* This way expects a word after the =
9795         p2 = getword(p, s, val);
9796         if (p2 <= p)
9797             {
9798             error("property file %s, line %d: expected value",
9799                     fileName.c_str(), linenr);
9800             return false;
9801             }
9802         */
9803         // This way gets the rest of the line after the =
9804         if (p>=len)
9805             {
9806             error("property file %s, line %d: expected value",
9807                     fileName.c_str(), linenr);
9808             return false;
9809             }
9810         val = s.substr(p);
9811         if (key.size()==0)
9812             continue;
9813         //allow property to be set, even if val=""
9815         //trace("key:'%s' val:'%s'", key.c_str(), val.c_str());
9816         //See if we wanted to overload this property
9817         std::map<String, String>::iterator iter =
9818             specifiedProperties.find(key);
9819         if (iter!=specifiedProperties.end())
9820             {
9821             val = iter->second;
9822             status("overloading property '%s' = '%s'",
9823                    key.c_str(), val.c_str());
9824             }
9825         properties[key] = val;
9826         }
9827     fclose(f);
9828     return true;
9834 /**
9835  *
9836  */
9837 bool Make::parseProperty(Element *elem)
9839     std::vector<Attribute> &attrs = elem->getAttributes();
9840     for (unsigned int i=0 ; i<attrs.size() ; i++)
9841         {
9842         String attrName = attrs[i].getName();
9843         String attrVal  = attrs[i].getValue();
9845         if (attrName == "name")
9846             {
9847             String val;
9848             if (!getAttribute(elem, "value", val))
9849                 return false;
9850             if (val.size() > 0)
9851                 {
9852                 properties[attrVal] = val;
9853                 }
9854             else
9855                 {
9856                 if (!getAttribute(elem, "location", val))
9857                     return false;
9858                 //let the property exist, even if not defined
9859                 properties[attrVal] = val;
9860                 }
9861             //See if we wanted to overload this property
9862             std::map<String, String>::iterator iter =
9863                 specifiedProperties.find(attrVal);
9864             if (iter != specifiedProperties.end())
9865                 {
9866                 val = iter->second;
9867                 status("overloading property '%s' = '%s'",
9868                     attrVal.c_str(), val.c_str());
9869                 properties[attrVal] = val;
9870                 }
9871             }
9872         else if (attrName == "file")
9873             {
9874             String prefix;
9875             if (!getAttribute(elem, "prefix", prefix))
9876                 return false;
9877             if (prefix.size() > 0)
9878                 {
9879                 if (prefix[prefix.size()-1] != '.')
9880                     prefix.push_back('.');
9881                 }
9882             if (!parsePropertyFile(attrName, prefix))
9883                 return false;
9884             }
9885         else if (attrName == "environment")
9886             {
9887             if (attrVal.find('.') != attrVal.npos)
9888                 {
9889                 error("environment prefix cannot have a '.' in it");
9890                 return false;
9891                 }
9892             envPrefix = attrVal;
9893             envPrefix.push_back('.');
9894             }
9895         else if (attrName == "pkg-config")
9896             {
9897             if (attrVal.find('.') != attrVal.npos)
9898                 {
9899                 error("pkg-config prefix cannot have a '.' in it");
9900                 return false;
9901                 }
9902             pcPrefix = attrVal;
9903             pcPrefix.push_back('.');
9904             }
9905         else if (attrName == "pkg-config-cflags")
9906             {
9907             if (attrVal.find('.') != attrVal.npos)
9908                 {
9909                 error("pkg-config-cflags prefix cannot have a '.' in it");
9910                 return false;
9911                 }
9912             pccPrefix = attrVal;
9913             pccPrefix.push_back('.');
9914             }
9915         else if (attrName == "pkg-config-libs")
9916             {
9917             if (attrVal.find('.') != attrVal.npos)
9918                 {
9919                 error("pkg-config-libs prefix cannot have a '.' in it");
9920                 return false;
9921                 }
9922             pclPrefix = attrVal;
9923             pclPrefix.push_back('.');
9924             }
9925         else if (attrName == "subversion")
9926             {
9927             if (attrVal.find('.') != attrVal.npos)
9928                 {
9929                 error("subversion prefix cannot have a '.' in it");
9930                 return false;
9931                 }
9932             svnPrefix = attrVal;
9933             svnPrefix.push_back('.');
9934             }
9935         }
9937     return true;
9943 /**
9944  *
9945  */
9946 bool Make::parseFile()
9948     status("######## PARSE : %s", uri.getPath().c_str());
9950     setLine(0);
9952     Parser parser;
9953     Element *root = parser.parseFile(uri.getNativePath());
9954     if (!root)
9955         {
9956         error("Could not open %s for reading",
9957               uri.getNativePath().c_str());
9958         return false;
9959         }
9960     
9961     setLine(root->getLine());
9963     if (root->getChildren().size()==0 ||
9964         root->getChildren()[0]->getName()!="project")
9965         {
9966         error("Main xml element should be <project>");
9967         delete root;
9968         return false;
9969         }
9971     //########## Project attributes
9972     Element *project = root->getChildren()[0];
9973     String s = project->getAttribute("name");
9974     if (s.size() > 0)
9975         projectName = s;
9976     s = project->getAttribute("default");
9977     if (s.size() > 0)
9978         defaultTarget = s;
9979     s = project->getAttribute("basedir");
9980     if (s.size() > 0)
9981         baseDir = s;
9983     //######### PARSE MEMBERS
9984     std::vector<Element *> children = project->getChildren();
9985     for (unsigned int i=0 ; i<children.size() ; i++)
9986         {
9987         Element *elem = children[i];
9988         setLine(elem->getLine());
9989         String tagName = elem->getName();
9991         //########## DESCRIPTION
9992         if (tagName == "description")
9993             {
9994             description = parser.trim(elem->getValue());
9995             }
9997         //######### PROPERTY
9998         else if (tagName == "property")
9999             {
10000             if (!parseProperty(elem))
10001                 return false;
10002             }
10004         //######### TARGET
10005         else if (tagName == "target")
10006             {
10007             String tname   = elem->getAttribute("name");
10008             String tdesc   = elem->getAttribute("description");
10009             String tdeps   = elem->getAttribute("depends");
10010             String tif     = elem->getAttribute("if");
10011             String tunless = elem->getAttribute("unless");
10012             Target target(*this);
10013             target.setName(tname);
10014             target.setDescription(tdesc);
10015             target.parseDependencies(tdeps);
10016             target.setIf(tif);
10017             target.setUnless(tunless);
10018             std::vector<Element *> telems = elem->getChildren();
10019             for (unsigned int i=0 ; i<telems.size() ; i++)
10020                 {
10021                 Element *telem = telems[i];
10022                 Task breeder(*this);
10023                 Task *task = breeder.createTask(telem, telem->getLine());
10024                 if (!task)
10025                     return false;
10026                 allTasks.push_back(task);
10027                 target.addTask(task);
10028                 }
10030             //Check name
10031             if (tname.size() == 0)
10032                 {
10033                 error("no name for target");
10034                 return false;
10035                 }
10036             //Check for duplicate name
10037             if (targets.find(tname) != targets.end())
10038                 {
10039                 error("target '%s' already defined", tname.c_str());
10040                 return false;
10041                 }
10042             //more work than targets[tname]=target, but avoids default allocator
10043             targets.insert(std::make_pair<String, Target>(tname, target));
10044             }
10045         //######### none of the above
10046         else
10047             {
10048             error("unknown toplevel tag: <%s>", tagName.c_str());
10049             return false;
10050             }
10052         }
10054     std::map<String, Target>::iterator iter;
10055     for (iter = targets.begin() ; iter!= targets.end() ; iter++)
10056         {
10057         Target tgt = iter->second;
10058         std::vector<String> depList;
10059         if (!checkTargetDependencies(tgt, depList))
10060             {
10061             return false;
10062             }
10063         }
10066     delete root;
10067     status("######## PARSE COMPLETE");
10068     return true;
10072 /**
10073  * Overload a <property>
10074  */
10075 bool Make::specifyProperty(const String &name, const String &value)
10077     if (specifiedProperties.find(name) != specifiedProperties.end())
10078         {
10079         error("Property %s already specified", name.c_str());
10080         return false;
10081         }
10082     specifiedProperties[name] = value;
10083     return true;
10088 /**
10089  *
10090  */
10091 bool Make::run()
10093     if (!parseFile())
10094         return false;
10095         
10096     if (!execute())
10097         return false;
10099     return true;
10105 /**
10106  * Get a formatted MM:SS.sss time elapsed string
10107  */ 
10108 static String
10109 timeDiffString(struct timeval &x, struct timeval &y)
10111     long microsX  = x.tv_usec;
10112     long secondsX = x.tv_sec;
10113     long microsY  = y.tv_usec;
10114     long secondsY = y.tv_sec;
10115     if (microsX < microsY)
10116         {
10117         microsX += 1000000;
10118         secondsX -= 1;
10119         }
10121     int seconds = (int)(secondsX - secondsY);
10122     int millis  = (int)((microsX - microsY)/1000);
10124     int minutes = seconds/60;
10125     seconds -= minutes*60;
10126     char buf[80];
10127     snprintf(buf, 79, "%dm %d.%03ds", minutes, seconds, millis);
10128     String ret = buf;
10129     return ret;
10130     
10133 /**
10134  *
10135  */
10136 bool Make::run(const String &target)
10138     status("####################################################");
10139     status("#   %s", version().c_str());
10140     status("####################################################");
10141     struct timeval timeStart, timeEnd;
10142     ::gettimeofday(&timeStart, NULL);
10143     specifiedTarget = target;
10144     if (!run())
10145         return false;
10146     ::gettimeofday(&timeEnd, NULL);
10147     String timeStr = timeDiffString(timeEnd, timeStart);
10148     status("####################################################");
10149     status("#   BuildTool Completed : %s", timeStr.c_str());
10150     status("####################################################");
10151     return true;
10160 }// namespace buildtool
10161 //########################################################################
10162 //# M A I N
10163 //########################################################################
10165 typedef buildtool::String String;
10167 /**
10168  *  Format an error message in printf() style
10169  */
10170 static void error(const char *fmt, ...)
10172     va_list ap;
10173     va_start(ap, fmt);
10174     fprintf(stderr, "BuildTool error: ");
10175     vfprintf(stderr, fmt, ap);
10176     fprintf(stderr, "\n");
10177     va_end(ap);
10181 static bool parseProperty(const String &s, String &name, String &val)
10183     int len = s.size();
10184     int i;
10185     for (i=0 ; i<len ; i++)
10186         {
10187         char ch = s[i];
10188         if (ch == '=')
10189             break;
10190         name.push_back(ch);
10191         }
10192     if (i>=len || s[i]!='=')
10193         {
10194         error("property requires -Dname=value");
10195         return false;
10196         }
10197     i++;
10198     for ( ; i<len ; i++)
10199         {
10200         char ch = s[i];
10201         val.push_back(ch);
10202         }
10203     return true;
10207 /**
10208  * Compare a buffer with a key, for the length of the key
10209  */
10210 static bool sequ(const String &buf, const char *key)
10212     int len = buf.size();
10213     for (int i=0 ; key[i] && i<len ; i++)
10214         {
10215         if (key[i] != buf[i])
10216             return false;
10217         }        
10218     return true;
10221 static void usage(int argc, char **argv)
10223     printf("usage:\n");
10224     printf("   %s [options] [target]\n", argv[0]);
10225     printf("Options:\n");
10226     printf("  -help, -h              print this message\n");
10227     printf("  -version               print the version information and exit\n");
10228     printf("  -file <file>           use given buildfile\n");
10229     printf("  -f <file>                 ''\n");
10230     printf("  -D<property>=<value>   use value for given property\n");
10236 /**
10237  * Parse the command-line args, get our options,
10238  * and run this thing
10239  */   
10240 static bool parseOptions(int argc, char **argv)
10242     if (argc < 1)
10243         {
10244         error("Cannot parse arguments");
10245         return false;
10246         }
10248     buildtool::Make make;
10250     String target;
10252     //char *progName = argv[0];
10253     for (int i=1 ; i<argc ; i++)
10254         {
10255         String arg = argv[i];
10256         if (arg.size()>1 && arg[0]=='-')
10257             {
10258             if (arg == "-h" || arg == "-help")
10259                 {
10260                 usage(argc,argv);
10261                 return true;
10262                 }
10263             else if (arg == "-version")
10264                 {
10265                 printf("%s", make.version().c_str());
10266                 return true;
10267                 }
10268             else if (arg == "-f" || arg == "-file")
10269                 {
10270                 if (i>=argc)
10271                    {
10272                    usage(argc, argv);
10273                    return false;
10274                    }
10275                 i++; //eat option
10276                 make.setURI(argv[i]);
10277                 }
10278             else if (arg.size()>2 && sequ(arg, "-D"))
10279                 {
10280                 String s = arg.substr(2, arg.size());
10281                 String name, value;
10282                 if (!parseProperty(s, name, value))
10283                    {
10284                    usage(argc, argv);
10285                    return false;
10286                    }
10287                 if (!make.specifyProperty(name, value))
10288                     return false;
10289                 }
10290             else
10291                 {
10292                 error("Unknown option:%s", arg.c_str());
10293                 return false;
10294                 }
10295             }
10296         else
10297             {
10298             if (target.size()>0)
10299                 {
10300                 error("only one initial target");
10301                 usage(argc, argv);
10302                 return false;
10303                 }
10304             target = arg;
10305             }
10306         }
10308     //We have the options.  Now execute them
10309     if (!make.run(target))
10310         return false;
10312     return true;
10319 static bool runMake()
10321     buildtool::Make make;
10322     if (!make.run())
10323         return false;
10324     return true;
10328 static bool pkgConfigTest()
10330     buildtool::PkgConfig pkgConfig;
10331     if (!pkgConfig.readFile("gtk+-2.0.pc"))
10332         return false;
10333     return true;
10338 static bool depTest()
10340     buildtool::DepTool deptool;
10341     deptool.setSourceDirectory("/dev/ink/inkscape/src");
10342     if (!deptool.generateDependencies("build.dep"))
10343         return false;
10344     std::vector<buildtool::FileRec> res =
10345            deptool.loadDepFile("build.dep");
10346     if (res.size() == 0)
10347         return false;
10348     return true;
10351 static bool popenTest()
10353     buildtool::Make make;
10354     buildtool::String out, err;
10355     bool ret = make.executeCommand("gcc xx.cpp", "", out, err);
10356     printf("Popen test:%d '%s' '%s'\n", ret, out.c_str(), err.c_str());
10357     return true;
10361 static bool propFileTest()
10363     buildtool::Make make;
10364     make.parsePropertyFile("test.prop", "test.");
10365     return true;
10369 int main(int argc, char **argv)
10372     if (!parseOptions(argc, argv))
10373         return 1;
10374     /*
10375     if (!popenTest())
10376         return 1;
10378     if (!depTest())
10379         return 1;
10380     if (!propFileTest())
10381         return 1;
10382     if (runMake())
10383         return 1;
10384     */
10385     return 0;
10389 //########################################################################
10390 //# E N D 
10391 //########################################################################