Code

Merging from trunk
[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 (!svnInfo.query(varname, val))
4708             return false;
4709         result = val;
4710         }
4711     else
4712         {
4713         std::map<String, String>::iterator iter;
4714         iter = properties.find(varname);
4715         if (iter != properties.end())
4716             {
4717             result = iter->second;
4718             }
4719         else
4720             {
4721             error("property '%s' not found", varname.c_str());
4722             return false;
4723             }
4724         }
4725     return true;
4731 /**
4732  * Analyse a string, looking for any substitutions or other
4733  * things that need resolution 
4734  */
4735 bool MakeBase::getSubstitutionsRecursive(const String &str,
4736                                          String &result, int depth)
4738     if (depth > 10)
4739         {
4740         error("nesting of substitutions too deep (>10) for '%s'",
4741                         str.c_str());
4742         return false;
4743         }
4744     String s = trim(str);
4745     int len = (int)s.size();
4746     String val;
4747     for (int i=0 ; i<len ; i++)
4748         {
4749         char ch = s[i];
4750         if (ch == '$' && s[i+1] == '{')
4751             {
4752             String varname;
4753             int j = i+2;
4754             for ( ; j<len ; j++)
4755                 {
4756                 ch = s[j];
4757                 if (ch == '$' && s[j+1] == '{')
4758                     {
4759                     error("attribute %s cannot have nested variable references",
4760                            s.c_str());
4761                     return false;
4762                     }
4763                 else if (ch == '}')
4764                     {
4765                     varname = trim(varname);
4766                     String varval;
4767                     if (!lookupProperty(varname, varval))
4768                         return false;
4769                     String varval2;
4770                     //Now see if the answer has ${} in it, too
4771                     if (!getSubstitutionsRecursive(varval, varval2, depth + 1))
4772                         return false;
4773                     val.append(varval2);
4774                     break;
4775                     }
4776                 else
4777                     {
4778                     varname.push_back(ch);
4779                     }
4780                 }
4781             i = j;
4782             }
4783         else
4784             {
4785             val.push_back(ch);
4786             }
4787         }
4788     result = val;
4789     return true;
4792 /**
4793  * Analyse a string, looking for any substitutions or other
4794  * things that need resilution 
4795  */
4796 bool MakeBase::getSubstitutions(const String &str, String &result)
4798     return getSubstitutionsRecursive(str, result, 0);
4803 /**
4804  * replace variable refs like ${a} with their values
4805  * Assume that the string has already been syntax validated
4806  */
4807 String MakeBase::eval(const String &s, const String &defaultVal)
4809     if (s.size()==0)
4810         return defaultVal;
4811     String ret;
4812     if (getSubstitutions(s, ret))
4813         return ret;
4814     else
4815         return defaultVal;
4819 /**
4820  * replace variable refs like ${a} with their values
4821  * return true or false
4822  * Assume that the string has already been syntax validated
4823  */
4824 bool MakeBase::evalBool(const String &s, bool defaultVal)
4826     if (s.size()==0)
4827         return defaultVal;
4828     String val = eval(s, "false");
4829     if (val.size()==0)
4830         return defaultVal;
4831     if (val == "true" || val == "TRUE")
4832         return true;
4833     else
4834         return false;
4838 /**
4839  * Get a string attribute, testing it for proper syntax and
4840  * property names.
4841  */
4842 bool MakeBase::getAttribute(Element *elem, const String &name,
4843                                     String &result)
4845     String s = elem->getAttribute(name);
4846     String tmp;
4847     bool ret = getSubstitutions(s, tmp);
4848     if (ret)
4849         result = s;  //assign -if- ok
4850     return ret;
4854 /**
4855  * Get a string value, testing it for proper syntax and
4856  * property names.
4857  */
4858 bool MakeBase::getValue(Element *elem, String &result)
4860     String s = elem->getValue();
4861     String tmp;
4862     bool ret = getSubstitutions(s, tmp);
4863     if (ret)
4864         result = s;  //assign -if- ok
4865     return ret;
4871 /**
4872  * Parse a <patternset> entry
4873  */  
4874 bool MakeBase::parsePatternSet(Element *elem,
4875                           MakeBase &propRef,
4876                           std::vector<String> &includes,
4877                           std::vector<String> &excludes
4878                           )
4880     std::vector<Element *> children  = elem->getChildren();
4881     for (unsigned int i=0 ; i<children.size() ; i++)
4882         {
4883         Element *child = children[i];
4884         String tagName = child->getName();
4885         if (tagName == "exclude")
4886             {
4887             String fname;
4888             if (!propRef.getAttribute(child, "name", fname))
4889                 return false;
4890             //trace("EXCLUDE: %s", fname.c_str());
4891             excludes.push_back(fname);
4892             }
4893         else if (tagName == "include")
4894             {
4895             String fname;
4896             if (!propRef.getAttribute(child, "name", fname))
4897                 return false;
4898             //trace("INCLUDE: %s", fname.c_str());
4899             includes.push_back(fname);
4900             }
4901         }
4903     return true;
4909 /**
4910  * Parse a <fileset> entry, and determine which files
4911  * should be included
4912  */  
4913 bool MakeBase::parseFileSet(Element *elem,
4914                           MakeBase &propRef,
4915                           FileSet &fileSet)
4917     String name = elem->getName();
4918     if (name != "fileset")
4919         {
4920         error("expected <fileset>");
4921         return false;
4922         }
4925     std::vector<String> includes;
4926     std::vector<String> excludes;
4928     //A fileset has one implied patternset
4929     if (!parsePatternSet(elem, propRef, includes, excludes))
4930         {
4931         return false;
4932         }
4933     //Look for child tags, including more patternsets
4934     std::vector<Element *> children  = elem->getChildren();
4935     for (unsigned int i=0 ; i<children.size() ; i++)
4936         {
4937         Element *child = children[i];
4938         String tagName = child->getName();
4939         if (tagName == "patternset")
4940             {
4941             if (!parsePatternSet(child, propRef, includes, excludes))
4942                 {
4943                 return false;
4944                 }
4945             }
4946         }
4948     String dir;
4949     //Now do the stuff
4950     //Get the base directory for reading file names
4951     if (!propRef.getAttribute(elem, "dir", dir))
4952         return false;
4954     fileSet.setDirectory(dir);
4955     fileSet.setIncludes(includes);
4956     fileSet.setExcludes(excludes);
4957     
4958     /*
4959     std::vector<String> fileList;
4960     if (dir.size() > 0)
4961         {
4962         String baseDir = propRef.resolve(dir);
4963         if (!listFiles(baseDir, "", includes, excludes, fileList))
4964             return false;
4965         }
4966     std::sort(fileList.begin(), fileList.end());
4967     result = fileList;
4968     */
4970     
4971     /*
4972     for (unsigned int i=0 ; i<result.size() ; i++)
4973         {
4974         trace("RES:%s", result[i].c_str());
4975         }
4976     */
4978     
4979     return true;
4982 /**
4983  * Parse a <filelist> entry.  This is far simpler than FileSet,
4984  * since no directory scanning is needed.  The file names are listed
4985  * explicitly.
4986  */  
4987 bool MakeBase::parseFileList(Element *elem,
4988                           MakeBase &propRef,
4989                           FileList &fileList)
4991     std::vector<String> fnames;
4992     //Look for child tags, namely "file"
4993     std::vector<Element *> children  = elem->getChildren();
4994     for (unsigned int i=0 ; i<children.size() ; i++)
4995         {
4996         Element *child = children[i];
4997         String tagName = child->getName();
4998         if (tagName == "file")
4999             {
5000             String fname = child->getAttribute("name");
5001             if (fname.size()==0)
5002                 {
5003                 error("<file> element requires name="" attribute");
5004                 return false;
5005                 }
5006             fnames.push_back(fname);
5007             }
5008         else
5009             {
5010             error("tag <%s> not allowed in <fileset>", tagName.c_str());
5011             return false;
5012             }
5013         }
5015     String dir;
5016     //Get the base directory for reading file names
5017     if (!propRef.getAttribute(elem, "dir", dir))
5018         return false;
5019     fileList.setDirectory(dir);
5020     fileList.setFiles(fnames);
5022     return true;
5027 /**
5028  * Create a directory, making intermediate dirs
5029  * if necessary
5030  */                  
5031 bool MakeBase::createDirectory(const String &dirname)
5033     //trace("## createDirectory: %s", dirname.c_str());
5034     //## first check if it exists
5035     struct stat finfo;
5036     String nativeDir = getNativePath(dirname);
5037     char *cnative = (char *) nativeDir.c_str();
5038 #ifdef __WIN32__
5039     if (strlen(cnative)==2 && cnative[1]==':')
5040         return true;
5041 #endif
5042     if (cachedStat(nativeDir, &finfo)==0)
5043         {
5044         if (!S_ISDIR(finfo.st_mode))
5045             {
5046             error("mkdir: file %s exists but is not a directory",
5047                   cnative);
5048             return false;
5049             }
5050         else //exists
5051             {
5052             return true;
5053             }
5054         }
5056     //## 2: pull off the last path segment, if any,
5057     //## to make the dir 'above' this one, if necessary
5058     unsigned int pos = dirname.find_last_of('/');
5059     if (pos>0 && pos != dirname.npos)
5060         {
5061         String subpath = dirname.substr(0, pos);
5062         //A letter root (c:) ?
5063         if (!createDirectory(subpath))
5064             return false;
5065         }
5066         
5067     //## 3: now make
5068 #ifdef __WIN32__
5069     if (mkdir(cnative)<0)
5070 #else
5071     if (mkdir(cnative, S_IRWXU | S_IRWXG | S_IRWXO)<0)
5072 #endif
5073         {
5074         error("cannot make directory '%s' : %s",
5075                  cnative, strerror(errno));
5076         return false;
5077         }
5079     removeFromStatCache(nativeDir);
5080         
5081     return true;
5085 /**
5086  * Remove a directory recursively
5087  */ 
5088 bool MakeBase::removeDirectory(const String &dirName)
5090     char *dname = (char *)dirName.c_str();
5092     DIR *dir = opendir(dname);
5093     if (!dir)
5094         {
5095         //# Let this fail nicely.
5096         return true;
5097         //error("error opening directory %s : %s", dname, strerror(errno));
5098         //return false;
5099         }
5100     
5101     while (true)
5102         {
5103         struct dirent *de = readdir(dir);
5104         if (!de)
5105             break;
5107         //Get the directory member name
5108         String s = de->d_name;
5109         if (s.size() == 0 || s[0] == '.')
5110             continue;
5111         String childName;
5112         if (dirName.size() > 0)
5113             {
5114             childName.append(dirName);
5115             childName.append("/");
5116             }
5117         childName.append(s);
5120         struct stat finfo;
5121         String childNative = getNativePath(childName);
5122         char *cnative = (char *)childNative.c_str();
5123         if (cachedStat(childNative, &finfo)<0)
5124             {
5125             error("cannot stat file:%s", cnative);
5126             }
5127         else if (S_ISDIR(finfo.st_mode))
5128             {
5129             //trace("DEL dir: %s", childName.c_str());
5130             if (!removeDirectory(childName))
5131                 {
5132                 return false;
5133                 }
5134             }
5135         else if (!S_ISREG(finfo.st_mode))
5136             {
5137             //trace("not regular: %s", cnative);
5138             }
5139         else
5140             {
5141             //trace("DEL file: %s", childName.c_str());
5142             if (!removeFile(childName))
5143                 {
5144                 return false;
5145                 }
5146             }
5147         }
5148     closedir(dir);
5150     //Now delete the directory
5151     String native = getNativePath(dirName);
5152     if (rmdir(native.c_str())<0)
5153         {
5154         error("could not delete directory %s : %s",
5155             native.c_str() , strerror(errno));
5156         return false;
5157         }
5159     removeFromStatCache(native);
5161     return true;
5162     
5166 /**
5167  * Copy a file from one name to another. Perform only if needed
5168  */ 
5169 bool MakeBase::copyFile(const String &srcFile, const String &destFile)
5171     //# 1 Check up-to-date times
5172     String srcNative = getNativePath(srcFile);
5173     struct stat srcinfo;
5174     if (cachedStat(srcNative, &srcinfo)<0)
5175         {
5176         error("source file %s for copy does not exist",
5177                  srcNative.c_str());
5178         return false;
5179         }
5181     String destNative = getNativePath(destFile);
5182     struct stat destinfo;
5183     if (cachedStat(destNative, &destinfo)==0)
5184         {
5185         if (destinfo.st_mtime >= srcinfo.st_mtime)
5186             return true;
5187         }
5188         
5189     //# 2 prepare a destination directory if necessary
5190     unsigned int pos = destFile.find_last_of('/');
5191     if (pos != destFile.npos)
5192         {
5193         String subpath = destFile.substr(0, pos);
5194         if (!createDirectory(subpath))
5195             return false;
5196         }
5198     //# 3 do the data copy
5199 #ifndef __WIN32__
5201     FILE *srcf = fopen(srcNative.c_str(), "rb");
5202     if (!srcf)
5203         {
5204         error("copyFile cannot open '%s' for reading", srcNative.c_str());
5205         return false;
5206         }
5207     FILE *destf = fopen(destNative.c_str(), "wb");
5208     if (!destf)
5209         {
5210         error("copyFile cannot open %s for writing", srcNative.c_str());
5211         return false;
5212         }
5214     while (!feof(srcf))
5215         {
5216         int ch = fgetc(srcf);
5217         if (ch<0)
5218             break;
5219         fputc(ch, destf);
5220         }
5222     fclose(destf);
5223     fclose(srcf);
5225 #else
5226     
5227     if (!CopyFile(srcNative.c_str(), destNative.c_str(), false))
5228         {
5229         error("copyFile from %s to %s failed",
5230              srcNative.c_str(), destNative.c_str());
5231         return false;
5232         }
5233         
5234 #endif /* __WIN32__ */
5236     removeFromStatCache(destNative);
5238     return true;
5242 /**
5243  * Delete a file
5244  */ 
5245 bool MakeBase::removeFile(const String &file)
5247     String native = getNativePath(file);
5249     if (!fileExists(native))
5250         {
5251         return true;
5252         }
5254 #ifdef WIN32
5255     // On Windows 'remove' will only delete files
5257     if (remove(native.c_str())<0)
5258         {
5259         if (errno==EACCES)
5260             {
5261             error("File %s is read-only", native.c_str());
5262             }
5263         else if (errno==ENOENT)
5264             {
5265             error("File %s does not exist or is a directory", native.c_str());
5266             }
5267         else
5268             {
5269             error("Failed to delete file %s: %s", native.c_str(), strerror(errno));
5270             }
5271         return false;
5272         }
5274 #else
5276     if (!isRegularFile(native))
5277         {
5278         error("File %s does not exist or is not a regular file", native.c_str());
5279         return false;
5280         }
5282     if (remove(native.c_str())<0)
5283         {
5284         if (errno==EACCES)
5285             {
5286             error("File %s is read-only", native.c_str());
5287             }
5288         else
5289             {
5290             error(
5291                 errno==EACCES ? "File %s is read-only" :
5292                 errno==ENOENT ? "File %s does not exist or is a directory" :
5293                 "Failed to delete file %s: %s", native.c_str());
5294             }
5295         return false;
5296         }
5298 #endif
5300     removeFromStatCache(native);
5302     return true;
5306 /**
5307  * Tests if the file exists
5308  */ 
5309 bool MakeBase::fileExists(const String &fileName)
5311     String native = getNativePath(fileName);
5312     struct stat finfo;
5313     
5314     //Exists?
5315     if (cachedStat(native, &finfo)<0)
5316         return false;
5318     return true;
5322 /**
5323  * Tests if the file exists and is a regular file
5324  */ 
5325 bool MakeBase::isRegularFile(const String &fileName)
5327     String native = getNativePath(fileName);
5328     struct stat finfo;
5329     
5330     //Exists?
5331     if (cachedStat(native, &finfo)<0)
5332         return false;
5335     //check the file mode
5336     if (!S_ISREG(finfo.st_mode))
5337         return false;
5339     return true;
5342 /**
5343  * Tests if the file exists and is a directory
5344  */ 
5345 bool MakeBase::isDirectory(const String &fileName)
5347     String native = getNativePath(fileName);
5348     struct stat finfo;
5349     
5350     //Exists?
5351     if (cachedStat(native, &finfo)<0)
5352         return false;
5355     //check the file mode
5356     if (!S_ISDIR(finfo.st_mode))
5357         return false;
5359     return true;
5364 /**
5365  * Tests is the modification of fileA is newer than fileB
5366  */ 
5367 bool MakeBase::isNewerThan(const String &fileA, const String &fileB)
5369     //trace("isNewerThan:'%s' , '%s'", fileA.c_str(), fileB.c_str());
5370     String nativeA = getNativePath(fileA);
5371     struct stat infoA;
5372     //IF source does not exist, NOT newer
5373     if (cachedStat(nativeA, &infoA)<0)
5374         {
5375         return false;
5376         }
5378     String nativeB = getNativePath(fileB);
5379     struct stat infoB;
5380     //IF dest does not exist, YES, newer
5381     if (cachedStat(nativeB, &infoB)<0)
5382         {
5383         return true;
5384         }
5386     //check the actual times
5387     if (infoA.st_mtime > infoB.st_mtime)
5388         {
5389         return true;
5390         }
5392     return false;
5396 //########################################################################
5397 //# P K G    C O N F I G
5398 //########################################################################
5401 /**
5402  * Get a character from the buffer at pos.  If out of range,
5403  * return -1 for safety
5404  */
5405 int PkgConfig::get(int pos)
5407     if (pos>parselen)
5408         return -1;
5409     return parsebuf[pos];
5414 /**
5415  *  Skip over all whitespace characters beginning at pos.  Return
5416  *  the position of the first non-whitespace character.
5417  *  Pkg-config is line-oriented, so check for newline
5418  */
5419 int PkgConfig::skipwhite(int pos)
5421     while (pos < parselen)
5422         {
5423         int ch = get(pos);
5424         if (ch < 0)
5425             break;
5426         if (!isspace(ch))
5427             break;
5428         pos++;
5429         }
5430     return pos;
5434 /**
5435  *  Parse the buffer beginning at pos, for a word.  Fill
5436  *  'ret' with the result.  Return the position after the
5437  *  word.
5438  */
5439 int PkgConfig::getword(int pos, String &ret)
5441     while (pos < parselen)
5442         {
5443         int ch = get(pos);
5444         if (ch < 0)
5445             break;
5446         if (!isalnum(ch) && ch != '_' && ch != '-' && ch != '+' && ch != '.')
5447             break;
5448         ret.push_back((char)ch);
5449         pos++;
5450         }
5451     return pos;
5454 bool PkgConfig::parseRequires()
5456     if (requires.size() == 0)
5457         return true;
5458     parsebuf = (char *)requires.c_str();
5459     parselen = requires.size();
5460     int pos = 0;
5461     while (pos < parselen)
5462         {
5463         pos = skipwhite(pos);
5464         String val;
5465         int pos2 = getword(pos, val);
5466         if (pos2 == pos)
5467             break;
5468         pos = pos2;
5469         //trace("val %s", val.c_str());
5470         requireList.push_back(val);
5471         }
5472     return true;
5476 static int getint(const String str)
5478     char *s = (char *)str.c_str();
5479     char *ends = NULL;
5480     long val = strtol(s, &ends, 10);
5481     if (ends == s)
5482         return 0L;
5483     else
5484         return val;
5487 void PkgConfig::parseVersion()
5489     if (version.size() == 0)
5490         return;
5491     String s1, s2, s3;
5492     unsigned int pos = 0;
5493     unsigned int pos2 = version.find('.', pos);
5494     if (pos2 == version.npos)
5495         {
5496         s1 = version;
5497         }
5498     else
5499         {
5500         s1 = version.substr(pos, pos2-pos);
5501         pos = pos2;
5502         pos++;
5503         if (pos < version.size())
5504             {
5505             pos2 = version.find('.', pos);
5506             if (pos2 == version.npos)
5507                 {
5508                 s2 = version.substr(pos, version.size()-pos);
5509                 }
5510             else
5511                 {
5512                 s2 = version.substr(pos, pos2-pos);
5513                 pos = pos2;
5514                 pos++;
5515                 if (pos < version.size())
5516                     s3 = version.substr(pos, pos2-pos);
5517                 }
5518             }
5519         }
5521     majorVersion = getint(s1);
5522     minorVersion = getint(s2);
5523     microVersion = getint(s3);
5524     //trace("version:%d.%d.%d", majorVersion,
5525     //          minorVersion, microVersion );
5529 bool PkgConfig::parseLine(const String &lineBuf)
5531     parsebuf = (char *)lineBuf.c_str();
5532     parselen = lineBuf.size();
5533     int pos = 0;
5534     
5535     while (pos < parselen)
5536         {
5537         String attrName;
5538         pos = skipwhite(pos);
5539         int ch = get(pos);
5540         if (ch == '#')
5541             {
5542             //comment.  eat the rest of the line
5543             while (pos < parselen)
5544                 {
5545                 ch = get(pos);
5546                 if (ch == '\n' || ch < 0)
5547                     break;
5548                 pos++;
5549                 }
5550             continue;
5551             }
5552         pos = getword(pos, attrName);
5553         if (attrName.size() == 0)
5554             continue;
5555         
5556         pos = skipwhite(pos);
5557         ch = get(pos);
5558         if (ch != ':' && ch != '=')
5559             {
5560             error("expected ':' or '='");
5561             return false;
5562             }
5563         pos++;
5564         pos = skipwhite(pos);
5565         String attrVal;
5566         while (pos < parselen)
5567             {
5568             ch = get(pos);
5569             if (ch == '\n' || ch < 0)
5570                 break;
5571             else if (ch == '$' && get(pos+1) == '{')
5572                 {
5573                 //#  this is a ${substitution}
5574                 pos += 2;
5575                 String subName;
5576                 while (pos < parselen)
5577                     {
5578                     ch = get(pos);
5579                     if (ch < 0)
5580                         {
5581                         error("unterminated substitution");
5582                         return false;
5583                         }
5584                     else if (ch == '}')
5585                         break;
5586                     else
5587                         subName.push_back((char)ch);
5588                     pos++;
5589                     }
5590                 //trace("subName:%s %s", subName.c_str(), prefix.c_str());
5591                 if (subName == "prefix" && prefix.size()>0)
5592                     {
5593                     attrVal.append(prefix);
5594                     //trace("prefix override:%s", prefix.c_str());
5595                     }
5596                 else
5597                     {
5598                     String subVal = attrs[subName];
5599                     //trace("subVal:%s", subVal.c_str());
5600                     attrVal.append(subVal);
5601                     }
5602                 }
5603             else
5604                 attrVal.push_back((char)ch);
5605             pos++;
5606             }
5608         attrVal = trim(attrVal);
5609         attrs[attrName] = attrVal;
5611         String attrNameL = toLower(attrName);
5613         if (attrNameL == "name")
5614             name = attrVal;
5615         else if (attrNameL == "description")
5616             description = attrVal;
5617         else if (attrNameL == "cflags")
5618             cflags = attrVal;
5619         else if (attrNameL == "libs")
5620             libs = attrVal;
5621         else if (attrNameL == "requires")
5622             requires = attrVal;
5623         else if (attrNameL == "version")
5624             version = attrVal;
5626         //trace("name:'%s'  value:'%s'",
5627         //      attrName.c_str(), attrVal.c_str());
5628         }
5630     return true;
5634 bool PkgConfig::parse(const String &buf)
5636     init();
5638     String line;
5639     int lineNr = 0;
5640     for (unsigned int p=0 ; p<buf.size() ; p++)
5641         {
5642         int ch = buf[p];
5643         if (ch == '\n' || ch == '\r')
5644             {
5645             if (!parseLine(line))
5646                 return false;
5647             line.clear();
5648             lineNr++;
5649             }
5650         else
5651             {
5652             line.push_back(ch);
5653             }
5654         }
5655     if (line.size()>0)
5656         {
5657         if (!parseLine(line))
5658             return false;
5659         }
5661     parseRequires();
5662     parseVersion();
5664     return true;
5670 void PkgConfig::dumpAttrs()
5672     //trace("### PkgConfig attributes for %s", fileName.c_str());
5673     std::map<String, String>::iterator iter;
5674     for (iter=attrs.begin() ; iter!=attrs.end() ; iter++)
5675         {
5676         trace("   %s = %s", iter->first.c_str(), iter->second.c_str());
5677         }
5681 bool PkgConfig::readFile(const String &fname)
5683     fileName = getNativePath(fname);
5685     FILE *f = fopen(fileName.c_str(), "r");
5686     if (!f)
5687         {
5688         error("cannot open file '%s' for reading", fileName.c_str());
5689         return false;
5690         }
5691     String buf;
5692     while (true)
5693         {
5694         int ch = fgetc(f);
5695         if (ch < 0)
5696             break;
5697         buf.push_back((char)ch);
5698         }
5699     fclose(f);
5701     //trace("####### File:\n%s", buf.c_str());
5702     if (!parse(buf))
5703         {
5704         return false;
5705         }
5707     //dumpAttrs();
5709     return true;
5714 bool PkgConfig::query(const String &pkgName)
5716     name = pkgName;
5718     String fname = path;
5719     fname.append("/");
5720     fname.append(name);
5721     fname.append(".pc");
5723     if (!readFile(fname))
5724         {
5725         error("Cannot find package '%s'. Do you have it installed?",
5726                        pkgName.c_str());
5727         return false;
5728         }
5729     
5730     return true;
5734 //########################################################################
5735 //# D E P T O O L
5736 //########################################################################
5740 /**
5741  *  Class which holds information for each file.
5742  */
5743 class FileRec
5745 public:
5747     typedef enum
5748         {
5749         UNKNOWN,
5750         CFILE,
5751         HFILE,
5752         OFILE
5753         } FileType;
5755     /**
5756      *  Constructor
5757      */
5758     FileRec()
5759         { init(); type = UNKNOWN; }
5761     /**
5762      *  Copy constructor
5763      */
5764     FileRec(const FileRec &other)
5765         { init(); assign(other); }
5766     /**
5767      *  Constructor
5768      */
5769     FileRec(int typeVal)
5770         { init(); type = typeVal; }
5771     /**
5772      *  Assignment operator
5773      */
5774     FileRec &operator=(const FileRec &other)
5775         { init(); assign(other); return *this; }
5778     /**
5779      *  Destructor
5780      */
5781     ~FileRec()
5782         {}
5784     /**
5785      *  Directory part of the file name
5786      */
5787     String path;
5789     /**
5790      *  Base name, sans directory and suffix
5791      */
5792     String baseName;
5794     /**
5795      *  File extension, such as cpp or h
5796      */
5797     String suffix;
5799     /**
5800      *  Type of file: CFILE, HFILE, OFILE
5801      */
5802     int type;
5804     /**
5805      * Used to list files ref'd by this one
5806      */
5807     std::map<String, FileRec *> files;
5810 private:
5812     void init()
5813         {
5814         }
5816     void assign(const FileRec &other)
5817         {
5818         type     = other.type;
5819         baseName = other.baseName;
5820         suffix   = other.suffix;
5821         files    = other.files;
5822         }
5824 };
5828 /**
5829  *  Simpler dependency record
5830  */
5831 class DepRec
5833 public:
5835     /**
5836      *  Constructor
5837      */
5838     DepRec()
5839         {init();}
5841     /**
5842      *  Copy constructor
5843      */
5844     DepRec(const DepRec &other)
5845         {init(); assign(other);}
5846     /**
5847      *  Constructor
5848      */
5849     DepRec(const String &fname)
5850         {init(); name = fname; }
5851     /**
5852      *  Assignment operator
5853      */
5854     DepRec &operator=(const DepRec &other)
5855         {init(); assign(other); return *this;}
5858     /**
5859      *  Destructor
5860      */
5861     ~DepRec()
5862         {}
5864     /**
5865      *  Directory part of the file name
5866      */
5867     String path;
5869     /**
5870      *  Base name, without the path and suffix
5871      */
5872     String name;
5874     /**
5875      *  Suffix of the source
5876      */
5877     String suffix;
5880     /**
5881      * Used to list files ref'd by this one
5882      */
5883     std::vector<String> files;
5886 private:
5888     void init()
5889         {
5890         }
5892     void assign(const DepRec &other)
5893         {
5894         path     = other.path;
5895         name     = other.name;
5896         suffix   = other.suffix;
5897         files    = other.files; //avoid recursion
5898         }
5900 };
5903 class DepTool : public MakeBase
5905 public:
5907     /**
5908      *  Constructor
5909      */
5910     DepTool()
5911         { init(); }
5913     /**
5914      *  Copy constructor
5915      */
5916     DepTool(const DepTool &other)
5917         { init(); assign(other); }
5919     /**
5920      *  Assignment operator
5921      */
5922     DepTool &operator=(const DepTool &other)
5923         { init(); assign(other); return *this; }
5926     /**
5927      *  Destructor
5928      */
5929     ~DepTool()
5930         {}
5933     /**
5934      *  Reset this section of code
5935      */
5936     virtual void init();
5937     
5938     /**
5939      *  Reset this section of code
5940      */
5941     virtual void assign(const DepTool &other)
5942         {
5943         }
5944     
5945     /**
5946      *  Sets the source directory which will be scanned
5947      */
5948     virtual void setSourceDirectory(const String &val)
5949         { sourceDir = val; }
5951     /**
5952      *  Returns the source directory which will be scanned
5953      */
5954     virtual String getSourceDirectory()
5955         { return sourceDir; }
5957     /**
5958      *  Sets the list of files within the directory to analyze
5959      */
5960     virtual void setFileList(const std::vector<String> &list)
5961         { fileList = list; }
5963     /**
5964      * Creates the list of all file names which will be
5965      * candidates for further processing.  Reads make.exclude
5966      * to see which files for directories to leave out.
5967      */
5968     virtual bool createFileList();
5971     /**
5972      *  Generates the forward dependency list
5973      */
5974     virtual bool generateDependencies();
5977     /**
5978      *  Generates the forward dependency list, saving the file
5979      */
5980     virtual bool generateDependencies(const String &);
5983     /**
5984      *  Load a dependency file
5985      */
5986     std::vector<DepRec> loadDepFile(const String &fileName);
5988     /**
5989      *  Load a dependency file, generating one if necessary
5990      */
5991     std::vector<DepRec> getDepFile(const String &fileName,
5992               bool forceRefresh);
5994     /**
5995      *  Save a dependency file
5996      */
5997     bool saveDepFile(const String &fileName);
6000 private:
6003     /**
6004      *
6005      */
6006     void parseName(const String &fullname,
6007                    String &path,
6008                    String &basename,
6009                    String &suffix);
6011     /**
6012      *
6013      */
6014     int get(int pos);
6016     /**
6017      *
6018      */
6019     int skipwhite(int pos);
6021     /**
6022      *
6023      */
6024     int getword(int pos, String &ret);
6026     /**
6027      *
6028      */
6029     bool sequ(int pos, const char *key);
6031     /**
6032      *
6033      */
6034     bool addIncludeFile(FileRec *frec, const String &fname);
6036     /**
6037      *
6038      */
6039     bool scanFile(const String &fname, FileRec *frec);
6041     /**
6042      *
6043      */
6044     bool processDependency(FileRec *ofile, FileRec *include);
6046     /**
6047      *
6048      */
6049     String sourceDir;
6051     /**
6052      *
6053      */
6054     std::vector<String> fileList;
6056     /**
6057      *
6058      */
6059     std::vector<String> directories;
6061     /**
6062      * A list of all files which will be processed for
6063      * dependencies.
6064      */
6065     std::map<String, FileRec *> allFiles;
6067     /**
6068      * The list of .o files, and the
6069      * dependencies upon them.
6070      */
6071     std::map<String, FileRec *> oFiles;
6073     int depFileSize;
6074     char *depFileBuf;
6076     static const int readBufSize = 8192;
6077     char readBuf[8193];//byte larger
6079 };
6085 /**
6086  *  Clean up after processing.  Called by the destructor, but should
6087  *  also be called before the object is reused.
6088  */
6089 void DepTool::init()
6091     sourceDir = ".";
6093     fileList.clear();
6094     directories.clear();
6095     
6096     //clear output file list
6097     std::map<String, FileRec *>::iterator iter;
6098     for (iter=oFiles.begin(); iter!=oFiles.end() ; iter++)
6099         delete iter->second;
6100     oFiles.clear();
6102     //allFiles actually contains the master copies. delete them
6103     for (iter= allFiles.begin(); iter!=allFiles.end() ; iter++)
6104         delete iter->second;
6105     allFiles.clear(); 
6112 /**
6113  *  Parse a full path name into path, base name, and suffix
6114  */
6115 void DepTool::parseName(const String &fullname,
6116                         String &path,
6117                         String &basename,
6118                         String &suffix)
6120     if (fullname.size() < 2)
6121         return;
6123     unsigned int pos = fullname.find_last_of('/');
6124     if (pos != fullname.npos && pos<fullname.size()-1)
6125         {
6126         path = fullname.substr(0, pos);
6127         pos++;
6128         basename = fullname.substr(pos, fullname.size()-pos);
6129         }
6130     else
6131         {
6132         path = "";
6133         basename = fullname;
6134         }
6136     pos = basename.find_last_of('.');
6137     if (pos != basename.npos && pos<basename.size()-1)
6138         {
6139         suffix   = basename.substr(pos+1, basename.size()-pos-1);
6140         basename = basename.substr(0, pos);
6141         }
6143     //trace("parsename:%s %s %s", path.c_str(),
6144     //        basename.c_str(), suffix.c_str()); 
6149 /**
6150  *  Generate our internal file list.
6151  */
6152 bool DepTool::createFileList()
6155     for (unsigned int i=0 ; i<fileList.size() ; i++)
6156         {
6157         String fileName = fileList[i];
6158         //trace("## FileName:%s", fileName.c_str());
6159         String path;
6160         String basename;
6161         String sfx;
6162         parseName(fileName, path, basename, sfx);
6163         if (sfx == "cpp" || sfx == "c" || sfx == "cxx"   ||
6164             sfx == "cc" || sfx == "CC")
6165             {
6166             FileRec *fe         = new FileRec(FileRec::CFILE);
6167             fe->path            = path;
6168             fe->baseName        = basename;
6169             fe->suffix          = sfx;
6170             allFiles[fileName]  = fe;
6171             }
6172         else if (sfx == "h"   ||  sfx == "hh"  ||
6173                  sfx == "hpp" ||  sfx == "hxx")
6174             {
6175             FileRec *fe         = new FileRec(FileRec::HFILE);
6176             fe->path            = path;
6177             fe->baseName        = basename;
6178             fe->suffix          = sfx;
6179             allFiles[fileName]  = fe;
6180             }
6181         }
6183     if (!listDirectories(sourceDir, "", directories))
6184         return false;
6185         
6186     return true;
6193 /**
6194  * Get a character from the buffer at pos.  If out of range,
6195  * return -1 for safety
6196  */
6197 int DepTool::get(int pos)
6199     if (pos>depFileSize)
6200         return -1;
6201     return depFileBuf[pos];
6206 /**
6207  *  Skip over all whitespace characters beginning at pos.  Return
6208  *  the position of the first non-whitespace character.
6209  */
6210 int DepTool::skipwhite(int pos)
6212     while (pos < depFileSize)
6213         {
6214         int ch = get(pos);
6215         if (ch < 0)
6216             break;
6217         if (!isspace(ch))
6218             break;
6219         pos++;
6220         }
6221     return pos;
6225 /**
6226  *  Parse the buffer beginning at pos, for a word.  Fill
6227  *  'ret' with the result.  Return the position after the
6228  *  word.
6229  */
6230 int DepTool::getword(int pos, String &ret)
6232     while (pos < depFileSize)
6233         {
6234         int ch = get(pos);
6235         if (ch < 0)
6236             break;
6237         if (isspace(ch))
6238             break;
6239         ret.push_back((char)ch);
6240         pos++;
6241         }
6242     return pos;
6245 /**
6246  * Return whether the sequence of characters in the buffer
6247  * beginning at pos match the key,  for the length of the key
6248  */
6249 bool DepTool::sequ(int pos, const char *key)
6251     while (*key)
6252         {
6253         if (*key != get(pos))
6254             return false;
6255         key++; pos++;
6256         }
6257     return true;
6262 /**
6263  *  Add an include file name to a file record.  If the name
6264  *  is not found in allFiles explicitly, try prepending include
6265  *  directory names to it and try again.
6266  */
6267 bool DepTool::addIncludeFile(FileRec *frec, const String &iname)
6269     //# if the name is an exact match to a path name
6270     //# in allFiles, like "myinc.h"
6271     std::map<String, FileRec *>::iterator iter =
6272            allFiles.find(iname);
6273     if (iter != allFiles.end()) //already exists
6274         {
6275          //h file in same dir
6276         FileRec *other = iter->second;
6277         //trace("local: '%s'", iname.c_str());
6278         frec->files[iname] = other;
6279         return true;
6280         }
6281     else 
6282         {
6283         //## Ok, it was not found directly
6284         //look in other dirs
6285         std::vector<String>::iterator diter;
6286         for (diter=directories.begin() ;
6287              diter!=directories.end() ; diter++)
6288             {
6289             String dfname = *diter;
6290             dfname.append("/");
6291             dfname.append(iname);
6292             URI fullPathURI(dfname);  //normalize path name
6293             String fullPath = fullPathURI.getPath();
6294             if (fullPath[0] == '/')
6295                 fullPath = fullPath.substr(1);
6296             //trace("Normalized %s to %s", dfname.c_str(), fullPath.c_str());
6297             iter = allFiles.find(fullPath);
6298             if (iter != allFiles.end())
6299                 {
6300                 FileRec *other = iter->second;
6301                 //trace("other: '%s'", iname.c_str());
6302                 frec->files[fullPath] = other;
6303                 return true;
6304                 }
6305             }
6306         }
6307     return true;
6312 /**
6313  *  Lightly parse a file to find the #include directives.  Do
6314  *  a bit of state machine stuff to make sure that the directive
6315  *  is valid.  (Like not in a comment).
6316  */
6317 bool DepTool::scanFile(const String &fname, FileRec *frec)
6319     String fileName;
6320     if (sourceDir.size() > 0)
6321         {
6322         fileName.append(sourceDir);
6323         fileName.append("/");
6324         }
6325     fileName.append(fname);
6326     String nativeName = getNativePath(fileName);
6327     FILE *f = fopen(nativeName.c_str(), "r");
6328     if (!f)
6329         {
6330         error("Could not open '%s' for reading", fname.c_str());
6331         return false;
6332         }
6333     String buf;
6334     while (!feof(f))
6335         {
6336         int nrbytes = fread(readBuf, 1, readBufSize, f);
6337         readBuf[nrbytes] = '\0';
6338         buf.append(readBuf);
6339         }
6340     fclose(f);
6342     depFileSize = buf.size();
6343     depFileBuf  = (char *)buf.c_str();
6344     int pos = 0;
6347     while (pos < depFileSize)
6348         {
6349         //trace("p:%c", get(pos));
6351         //# Block comment
6352         if (get(pos) == '/' && get(pos+1) == '*')
6353             {
6354             pos += 2;
6355             while (pos < depFileSize)
6356                 {
6357                 if (get(pos) == '*' && get(pos+1) == '/')
6358                     {
6359                     pos += 2;
6360                     break;
6361                     }
6362                 else
6363                     pos++;
6364                 }
6365             }
6366         //# Line comment
6367         else if (get(pos) == '/' && get(pos+1) == '/')
6368             {
6369             pos += 2;
6370             while (pos < depFileSize)
6371                 {
6372                 if (get(pos) == '\n')
6373                     {
6374                     pos++;
6375                     break;
6376                     }
6377                 else
6378                     pos++;
6379                 }
6380             }
6381         //# #include! yaay
6382         else if (sequ(pos, "#include"))
6383             {
6384             pos += 8;
6385             pos = skipwhite(pos);
6386             String iname;
6387             pos = getword(pos, iname);
6388             if (iname.size()>2)
6389                 {
6390                 iname = iname.substr(1, iname.size()-2);
6391                 addIncludeFile(frec, iname);
6392                 }
6393             }
6394         else
6395             {
6396             pos++;
6397             }
6398         }
6400     return true;
6405 /**
6406  *  Recursively check include lists to find all files in allFiles to which
6407  *  a given file is dependent.
6408  */
6409 bool DepTool::processDependency(FileRec *ofile, FileRec *include)
6411     std::map<String, FileRec *>::iterator iter;
6412     for (iter=include->files.begin() ; iter!=include->files.end() ; iter++)
6413         {
6414         String fname  = iter->first;
6415         if (ofile->files.find(fname) != ofile->files.end())
6416             {
6417             //trace("file '%s' already seen", fname.c_str());
6418             continue;
6419             }
6420         FileRec *child  = iter->second;
6421         ofile->files[fname] = child;
6422       
6423         processDependency(ofile, child);
6424         }
6427     return true;
6434 /**
6435  *  Generate the file dependency list.
6436  */
6437 bool DepTool::generateDependencies()
6439     std::map<String, FileRec *>::iterator iter;
6440     //# First pass.  Scan for all includes
6441     for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
6442         {
6443         FileRec *frec = iter->second;
6444         if (!scanFile(iter->first, frec))
6445             {
6446             //quit?
6447             }
6448         }
6450     //# Second pass.  Scan for all includes
6451     for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
6452         {
6453         FileRec *include = iter->second;
6454         if (include->type == FileRec::CFILE)
6455             {
6456             //String cFileName   = iter->first;
6457             FileRec *ofile     = new FileRec(FileRec::OFILE);
6458             ofile->path        = include->path;
6459             ofile->baseName    = include->baseName;
6460             ofile->suffix      = include->suffix;
6461             String fname       = include->path;
6462             if (fname.size()>0)
6463                 fname.append("/");
6464             fname.append(include->baseName);
6465             fname.append(".o");
6466             oFiles[fname]    = ofile;
6467             //add the .c file first?   no, don't
6468             //ofile->files[cFileName] = include;
6469             
6470             //trace("ofile:%s", fname.c_str());
6472             processDependency(ofile, include);
6473             }
6474         }
6476       
6477     return true;
6482 /**
6483  *  High-level call to generate deps and optionally save them
6484  */
6485 bool DepTool::generateDependencies(const String &fileName)
6487     if (!createFileList())
6488         return false;
6489     if (!generateDependencies())
6490         return false;
6491     if (!saveDepFile(fileName))
6492         return false;
6493     return true;
6497 /**
6498  *   This saves the dependency cache.
6499  */
6500 bool DepTool::saveDepFile(const String &fileName)
6502     time_t tim;
6503     time(&tim);
6505     FILE *f = fopen(fileName.c_str(), "w");
6506     if (!f)
6507         {
6508         trace("cannot open '%s' for writing", fileName.c_str());
6509         }
6510     fprintf(f, "<?xml version='1.0'?>\n");
6511     fprintf(f, "<!--\n");
6512     fprintf(f, "########################################################\n");
6513     fprintf(f, "## File: build.dep\n");
6514     fprintf(f, "## Generated by BuildTool at :%s", ctime(&tim));
6515     fprintf(f, "########################################################\n");
6516     fprintf(f, "-->\n");
6518     fprintf(f, "<dependencies source='%s'>\n\n", sourceDir.c_str());
6519     std::map<String, FileRec *>::iterator iter;
6520     for (iter=oFiles.begin() ; iter!=oFiles.end() ; iter++)
6521         {
6522         FileRec *frec = iter->second;
6523         if (frec->type == FileRec::OFILE)
6524             {
6525             fprintf(f, "<object path='%s' name='%s' suffix='%s'>\n",
6526                  frec->path.c_str(), frec->baseName.c_str(), frec->suffix.c_str());
6527             std::map<String, FileRec *>::iterator citer;
6528             for (citer=frec->files.begin() ; citer!=frec->files.end() ; citer++)
6529                 {
6530                 String cfname = citer->first;
6531                 fprintf(f, "    <dep name='%s'/>\n", cfname.c_str());
6532                 }
6533             fprintf(f, "</object>\n\n");
6534             }
6535         }
6537     fprintf(f, "</dependencies>\n");
6538     fprintf(f, "\n");
6539     fprintf(f, "<!--\n");
6540     fprintf(f, "########################################################\n");
6541     fprintf(f, "## E N D\n");
6542     fprintf(f, "########################################################\n");
6543     fprintf(f, "-->\n");
6545     fclose(f);
6547     return true;
6553 /**
6554  *   This loads the dependency cache.
6555  */
6556 std::vector<DepRec> DepTool::loadDepFile(const String &depFile)
6558     std::vector<DepRec> result;
6559     
6560     Parser parser;
6561     Element *root = parser.parseFile(depFile.c_str());
6562     if (!root)
6563         {
6564         //error("Could not open %s for reading", depFile.c_str());
6565         return result;
6566         }
6568     if (root->getChildren().size()==0 ||
6569         root->getChildren()[0]->getName()!="dependencies")
6570         {
6571         error("loadDepFile: main xml element should be <dependencies>");
6572         delete root;
6573         return result;
6574         }
6576     //########## Start parsing
6577     Element *depList = root->getChildren()[0];
6579     std::vector<Element *> objects = depList->getChildren();
6580     for (unsigned int i=0 ; i<objects.size() ; i++)
6581         {
6582         Element *objectElem = objects[i];
6583         String tagName = objectElem->getName();
6584         if (tagName != "object")
6585             {
6586             error("loadDepFile: <dependencies> should have only <object> children");
6587             return result;
6588             }
6590         String objName   = objectElem->getAttribute("name");
6591          //trace("object:%s", objName.c_str());
6592         DepRec depObject(objName);
6593         depObject.path   = objectElem->getAttribute("path");
6594         depObject.suffix = objectElem->getAttribute("suffix");
6595         //########## DESCRIPTION
6596         std::vector<Element *> depElems = objectElem->getChildren();
6597         for (unsigned int i=0 ; i<depElems.size() ; i++)
6598             {
6599             Element *depElem = depElems[i];
6600             tagName = depElem->getName();
6601             if (tagName != "dep")
6602                 {
6603                 error("loadDepFile: <object> should have only <dep> children");
6604                 return result;
6605                 }
6606             String depName = depElem->getAttribute("name");
6607             //trace("    dep:%s", depName.c_str());
6608             depObject.files.push_back(depName);
6609             }
6611         //Insert into the result list, in a sorted manner
6612         bool inserted = false;
6613         std::vector<DepRec>::iterator iter;
6614         for (iter = result.begin() ; iter != result.end() ; iter++)
6615             {
6616             String vpath = iter->path;
6617             vpath.append("/");
6618             vpath.append(iter->name);
6619             String opath = depObject.path;
6620             opath.append("/");
6621             opath.append(depObject.name);
6622             if (vpath > opath)
6623                 {
6624                 inserted = true;
6625                 iter = result.insert(iter, depObject);
6626                 break;
6627                 }
6628             }
6629         if (!inserted)
6630             result.push_back(depObject);
6631         }
6633     delete root;
6635     return result;
6639 /**
6640  *   This loads the dependency cache.
6641  */
6642 std::vector<DepRec> DepTool::getDepFile(const String &depFile,
6643                    bool forceRefresh)
6645     std::vector<DepRec> result;
6646     if (forceRefresh)
6647         {
6648         generateDependencies(depFile);
6649         result = loadDepFile(depFile);
6650         }
6651     else
6652         {
6653         //try once
6654         result = loadDepFile(depFile);
6655         if (result.size() == 0)
6656             {
6657             //fail? try again
6658             generateDependencies(depFile);
6659             result = loadDepFile(depFile);
6660             }
6661         }
6662     return result;
6668 //########################################################################
6669 //# T A S K
6670 //########################################################################
6671 //forward decl
6672 class Target;
6673 class Make;
6675 /**
6676  *
6677  */
6678 class Task : public MakeBase
6681 public:
6683     typedef enum
6684         {
6685         TASK_NONE,
6686         TASK_CC,
6687         TASK_COPY,
6688         TASK_CXXTEST_PART,
6689         TASK_CXXTEST_ROOT,
6690         TASK_CXXTEST_RUN,
6691         TASK_DELETE,
6692         TASK_ECHO,
6693         TASK_JAR,
6694         TASK_JAVAC,
6695         TASK_LINK,
6696         TASK_MAKEFILE,
6697         TASK_MKDIR,
6698         TASK_MSGFMT,
6699         TASK_PKG_CONFIG,
6700         TASK_RANLIB,
6701         TASK_RC,
6702         TASK_SHAREDLIB,
6703         TASK_STATICLIB,
6704         TASK_STRIP,
6705         TASK_TOUCH,
6706         TASK_TSTAMP
6707         } TaskType;
6708         
6710     /**
6711      *
6712      */
6713     Task(MakeBase &par) : parent(par)
6714         { init(); }
6716     /**
6717      *
6718      */
6719     Task(const Task &other) : parent(other.parent)
6720         { init(); assign(other); }
6722     /**
6723      *
6724      */
6725     Task &operator=(const Task &other)
6726         { assign(other); return *this; }
6728     /**
6729      *
6730      */
6731     virtual ~Task()
6732         { }
6735     /**
6736      *
6737      */
6738     virtual MakeBase &getParent()
6739         { return parent; }
6741      /**
6742      *
6743      */
6744     virtual int  getType()
6745         { return type; }
6747     /**
6748      *
6749      */
6750     virtual void setType(int val)
6751         { type = val; }
6753     /**
6754      *
6755      */
6756     virtual String getName()
6757         { return name; }
6759     /**
6760      *
6761      */
6762     virtual bool execute()
6763         { return true; }
6765     /**
6766      *
6767      */
6768     virtual bool parse(Element *elem)
6769         { return true; }
6771     /**
6772      *
6773      */
6774     Task *createTask(Element *elem, int lineNr);
6777 protected:
6779     void init()
6780         {
6781         type = TASK_NONE;
6782         name = "none";
6783         }
6785     void assign(const Task &other)
6786         {
6787         type = other.type;
6788         name = other.name;
6789         }
6790         
6791     /**
6792      *  Show task status
6793      */
6794     void taskstatus(const char *fmt, ...)
6795         {
6796         va_list args;
6797         va_start(args,fmt);
6798         fprintf(stdout, "    %s : ", name.c_str());
6799         vfprintf(stdout, fmt, args);
6800         fprintf(stdout, "\n");
6801         va_end(args) ;
6802         }
6804     String getAttribute(Element *elem, const String &attrName)
6805         {
6806         String str;
6807         return str;
6808         }
6810     MakeBase &parent;
6812     int type;
6814     String name;
6815 };
6819 /**
6820  * This task runs the C/C++ compiler.  The compiler is invoked
6821  * for all .c or .cpp files which are newer than their correcsponding
6822  * .o files.  
6823  */
6824 class TaskCC : public Task
6826 public:
6828     TaskCC(MakeBase &par) : Task(par)
6829         {
6830         type = TASK_CC;
6831         name = "cc";
6832         }
6834     virtual ~TaskCC()
6835         {}
6836         
6837     virtual bool isExcludedInc(const String &dirname)
6838         {
6839         for (unsigned int i=0 ; i<excludeInc.size() ; i++)
6840             {
6841             String fname = excludeInc[i];
6842             if (fname == dirname)
6843                 return true;
6844             }
6845         return false;
6846         }
6848     virtual bool execute()
6849         {
6850         //evaluate our parameters
6851         String command         = parent.eval(commandOpt, "gcc");
6852         String ccCommand       = parent.eval(ccCommandOpt, "gcc");
6853         String cxxCommand      = parent.eval(cxxCommandOpt, "g++");
6854         String source          = parent.eval(sourceOpt, ".");
6855         String dest            = parent.eval(destOpt, ".");
6856         String flags           = parent.eval(flagsOpt, "");
6857         String defines         = parent.eval(definesOpt, "");
6858         String includes        = parent.eval(includesOpt, "");
6859         bool continueOnError   = parent.evalBool(continueOnErrorOpt, true);
6860         bool refreshCache      = parent.evalBool(refreshCacheOpt, false);
6862         if (!listFiles(parent, fileSet))
6863             return false;
6864             
6865         FILE *f = NULL;
6866         f = fopen("compile.lst", "w");
6868         //refreshCache is probably false here, unless specified otherwise
6869         String fullName = parent.resolve("build.dep");
6870         if (refreshCache || isNewerThan(parent.getURI().getPath(), fullName))
6871             {
6872             taskstatus("regenerating C/C++ dependency cache");
6873             refreshCache = true;
6874             }
6876         DepTool depTool;
6877         depTool.setSourceDirectory(source);
6878         depTool.setFileList(fileSet.getFiles());
6879         std::vector<DepRec> deps =
6880              depTool.getDepFile("build.dep", refreshCache);
6881         
6882         String incs;
6883         incs.append("-I");
6884         incs.append(parent.resolve("."));
6885         incs.append(" ");
6886         if (includes.size()>0)
6887             {
6888             incs.append(includes);
6889             incs.append(" ");
6890             }
6891         std::set<String> paths;
6892         std::vector<DepRec>::iterator viter;
6893         for (viter=deps.begin() ; viter!=deps.end() ; viter++)
6894             {
6895             DepRec dep = *viter;
6896             if (dep.path.size()>0)
6897                 paths.insert(dep.path);
6898             }
6899         if (source.size()>0)
6900             {
6901             incs.append(" -I");
6902             incs.append(parent.resolve(source));
6903             incs.append(" ");
6904             }
6905         std::set<String>::iterator setIter;
6906         for (setIter=paths.begin() ; setIter!=paths.end() ; setIter++)
6907             {
6908             String dirName = *setIter;
6909             //check excludeInc to see if we dont want to include this dir
6910             if (isExcludedInc(dirName))
6911                 continue;
6912             incs.append(" -I");
6913             String dname;
6914             if (source.size()>0)
6915                 {
6916                 dname.append(source);
6917                 dname.append("/");
6918                 }
6919             dname.append(dirName);
6920             incs.append(parent.resolve(dname));
6921             }
6922             
6923         /**
6924          * Compile each of the C files that need it
6925          */
6926         bool errorOccurred = false;                 
6927         std::vector<String> cfiles;
6928         for (viter=deps.begin() ; viter!=deps.end() ; viter++)
6929             {
6930             DepRec dep = *viter;
6932             //## Select command
6933             String sfx = dep.suffix;
6934             String command = ccCommand;
6935             if (sfx == "cpp" || sfx == "cxx" || sfx == "c++" ||
6936                  sfx == "cc" || sfx == "CC")
6937                 command = cxxCommand;
6938  
6939             //## Make paths
6940             String destPath = dest;
6941             String srcPath  = source;
6942             if (dep.path.size()>0)
6943                 {
6944                 destPath.append("/");
6945                 destPath.append(dep.path);
6946                 srcPath.append("/");
6947                 srcPath.append(dep.path);
6948                 }
6949             //## Make sure destination directory exists
6950             if (!createDirectory(destPath))
6951                 return false;
6952                 
6953             //## Check whether it needs to be done
6954             String destName;
6955             if (destPath.size()>0)
6956                 {
6957                 destName.append(destPath);
6958                 destName.append("/");
6959                 }
6960             destName.append(dep.name);
6961             destName.append(".o");
6962             String destFullName = parent.resolve(destName);
6963             String srcName;
6964             if (srcPath.size()>0)
6965                 {
6966                 srcName.append(srcPath);
6967                 srcName.append("/");
6968                 }
6969             srcName.append(dep.name);
6970             srcName.append(".");
6971             srcName.append(dep.suffix);
6972             String srcFullName = parent.resolve(srcName);
6973             bool compileMe = false;
6974             //# First we check if the source is newer than the .o
6975             if (isNewerThan(srcFullName, destFullName))
6976                 {
6977                 taskstatus("compile of %s required by source: %s",
6978                         destFullName.c_str(), srcFullName.c_str());
6979                 compileMe = true;
6980                 }
6981             else
6982                 {
6983                 //# secondly, we check if any of the included dependencies
6984                 //# of the .c/.cpp is newer than the .o
6985                 for (unsigned int i=0 ; i<dep.files.size() ; i++)
6986                     {
6987                     String depName;
6988                     if (source.size()>0)
6989                         {
6990                         depName.append(source);
6991                         depName.append("/");
6992                         }
6993                     depName.append(dep.files[i]);
6994                     String depFullName = parent.resolve(depName);
6995                     bool depRequires = isNewerThan(depFullName, destFullName);
6996                     //trace("%d %s %s\n", depRequires,
6997                     //        destFullName.c_str(), depFullName.c_str());
6998                     if (depRequires)
6999                         {
7000                         taskstatus("compile of %s required by included: %s",
7001                                 destFullName.c_str(), depFullName.c_str());
7002                         compileMe = true;
7003                         break;
7004                         }
7005                     }
7006                 }
7007             if (!compileMe)
7008                 {
7009                 continue;
7010                 }
7012             //## Assemble the command
7013             String cmd = command;
7014             cmd.append(" -c ");
7015             cmd.append(flags);
7016             cmd.append(" ");
7017             cmd.append(defines);
7018             cmd.append(" ");
7019             cmd.append(incs);
7020             cmd.append(" ");
7021             cmd.append(srcFullName);
7022             cmd.append(" -o ");
7023             cmd.append(destFullName);
7025             //## Execute the command
7027             String outString, errString;
7028             bool ret = executeCommand(cmd.c_str(), "", outString, errString);
7030             if (f)
7031                 {
7032                 fprintf(f, "########################### File : %s\n",
7033                              srcFullName.c_str());
7034                 fprintf(f, "#### COMMAND ###\n");
7035                 int col = 0;
7036                 for (unsigned int i = 0 ; i < cmd.size() ; i++)
7037                     {
7038                     char ch = cmd[i];
7039                     if (isspace(ch)  && col > 63)
7040                         {
7041                         fputc('\n', f);
7042                         col = 0;
7043                         }
7044                     else
7045                         {
7046                         fputc(ch, f);
7047                         col++;
7048                         }
7049                     if (col > 76)
7050                         {
7051                         fputc('\n', f);
7052                         col = 0;
7053                         }
7054                     }
7055                 fprintf(f, "\n");
7056                 fprintf(f, "#### STDOUT ###\n%s\n", outString.c_str());
7057                 fprintf(f, "#### STDERR ###\n%s\n\n", errString.c_str());
7058                 fflush(f);
7059                 }
7060             if (!ret)
7061                 {
7062                 error("problem compiling: %s", errString.c_str());
7063                 errorOccurred = true;
7064                 }
7065             if (errorOccurred && !continueOnError)
7066                 break;
7068             removeFromStatCache(getNativePath(destFullName));
7069             }
7071         if (f)
7072             {
7073             fclose(f);
7074             }
7075         
7076         return !errorOccurred;
7077         }
7080     virtual bool parse(Element *elem)
7081         {
7082         String s;
7083         if (!parent.getAttribute(elem, "command", commandOpt))
7084             return false;
7085         if (commandOpt.size()>0)
7086             { cxxCommandOpt = ccCommandOpt = commandOpt; }
7087         if (!parent.getAttribute(elem, "cc", ccCommandOpt))
7088             return false;
7089         if (!parent.getAttribute(elem, "cxx", cxxCommandOpt))
7090             return false;
7091         if (!parent.getAttribute(elem, "destdir", destOpt))
7092             return false;
7093         if (!parent.getAttribute(elem, "continueOnError", continueOnErrorOpt))
7094             return false;
7095         if (!parent.getAttribute(elem, "refreshCache", refreshCacheOpt))
7096             return false;
7098         std::vector<Element *> children = elem->getChildren();
7099         for (unsigned int i=0 ; i<children.size() ; i++)
7100             {
7101             Element *child = children[i];
7102             String tagName = child->getName();
7103             if (tagName == "flags")
7104                 {
7105                 if (!parent.getValue(child, flagsOpt))
7106                     return false;
7107                 flagsOpt = strip(flagsOpt);
7108                 }
7109             else if (tagName == "includes")
7110                 {
7111                 if (!parent.getValue(child, includesOpt))
7112                     return false;
7113                 includesOpt = strip(includesOpt);
7114                 }
7115             else if (tagName == "defines")
7116                 {
7117                 if (!parent.getValue(child, definesOpt))
7118                     return false;
7119                 definesOpt = strip(definesOpt);
7120                 }
7121             else if (tagName == "fileset")
7122                 {
7123                 if (!parseFileSet(child, parent, fileSet))
7124                     return false;
7125                 sourceOpt = fileSet.getDirectory();
7126                 }
7127             else if (tagName == "excludeinc")
7128                 {
7129                 if (!parseFileList(child, parent, excludeInc))
7130                     return false;
7131                 }
7132             }
7134         return true;
7135         }
7136         
7137 protected:
7139     String   commandOpt;
7140     String   ccCommandOpt;
7141     String   cxxCommandOpt;
7142     String   sourceOpt;
7143     String   destOpt;
7144     String   flagsOpt;
7145     String   definesOpt;
7146     String   includesOpt;
7147     String   continueOnErrorOpt;
7148     String   refreshCacheOpt;
7149     FileSet  fileSet;
7150     FileList excludeInc;
7151     
7152 };
7156 /**
7157  *
7158  */
7159 class TaskCopy : public Task
7161 public:
7163     typedef enum
7164         {
7165         CP_NONE,
7166         CP_TOFILE,
7167         CP_TODIR
7168         } CopyType;
7170     TaskCopy(MakeBase &par) : Task(par)
7171         {
7172         type        = TASK_COPY;
7173         name        = "copy";
7174         cptype      = CP_NONE;
7175         haveFileSet = false;
7176         }
7178     virtual ~TaskCopy()
7179         {}
7181     virtual bool execute()
7182         {
7183         String fileName   = parent.eval(fileNameOpt   , ".");
7184         String toFileName = parent.eval(toFileNameOpt , ".");
7185         String toDirName  = parent.eval(toDirNameOpt  , ".");
7186         bool   verbose    = parent.evalBool(verboseOpt, false);
7187         switch (cptype)
7188            {
7189            case CP_TOFILE:
7190                {
7191                if (fileName.size()>0)
7192                    {
7193                    taskstatus("%s to %s",
7194                         fileName.c_str(), toFileName.c_str());
7195                    String fullSource = parent.resolve(fileName);
7196                    String fullDest = parent.resolve(toFileName);
7197                    if (verbose)
7198                        taskstatus("copy %s to file %s", fullSource.c_str(),
7199                                           fullDest.c_str());
7200                    if (!isRegularFile(fullSource))
7201                        {
7202                        error("copy : file %s does not exist", fullSource.c_str());
7203                        return false;
7204                        }
7205                    if (!isNewerThan(fullSource, fullDest))
7206                        {
7207                        taskstatus("skipped");
7208                        return true;
7209                        }
7210                    if (!copyFile(fullSource, fullDest))
7211                        return false;
7212                    taskstatus("1 file copied");
7213                    }
7214                return true;
7215                }
7216            case CP_TODIR:
7217                {
7218                if (haveFileSet)
7219                    {
7220                    if (!listFiles(parent, fileSet))
7221                        return false;
7222                    String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
7224                    taskstatus("%s to %s",
7225                        fileSetDir.c_str(), toDirName.c_str());
7227                    int nrFiles = 0;
7228                    for (unsigned int i=0 ; i<fileSet.size() ; i++)
7229                        {
7230                        String fileName = fileSet[i];
7232                        String sourcePath;
7233                        if (fileSetDir.size()>0)
7234                            {
7235                            sourcePath.append(fileSetDir);
7236                            sourcePath.append("/");
7237                            }
7238                        sourcePath.append(fileName);
7239                        String fullSource = parent.resolve(sourcePath);
7240                        
7241                        //Get the immediate parent directory's base name
7242                        String baseFileSetDir = fileSetDir;
7243                        unsigned int pos = baseFileSetDir.find_last_of('/');
7244                        if (pos!=baseFileSetDir.npos &&
7245                                   pos < baseFileSetDir.size()-1)
7246                            baseFileSetDir =
7247                               baseFileSetDir.substr(pos+1,
7248                                    baseFileSetDir.size());
7249                        //Now make the new path
7250                        String destPath;
7251                        if (toDirName.size()>0)
7252                            {
7253                            destPath.append(toDirName);
7254                            destPath.append("/");
7255                            }
7256                        if (baseFileSetDir.size()>0)
7257                            {
7258                            destPath.append(baseFileSetDir);
7259                            destPath.append("/");
7260                            }
7261                        destPath.append(fileName);
7262                        String fullDest = parent.resolve(destPath);
7263                        //trace("fileName:%s", fileName.c_str());
7264                        if (verbose)
7265                            taskstatus("copy %s to new dir : %s",
7266                                  fullSource.c_str(), fullDest.c_str());
7267                        if (!isNewerThan(fullSource, fullDest))
7268                            {
7269                            if (verbose)
7270                                taskstatus("copy skipping %s", fullSource.c_str());
7271                            continue;
7272                            }
7273                        if (!copyFile(fullSource, fullDest))
7274                            return false;
7275                        nrFiles++;
7276                        }
7277                    taskstatus("%d file(s) copied", nrFiles);
7278                    }
7279                else //file source
7280                    {
7281                    //For file->dir we want only the basename of
7282                    //the source appended to the dest dir
7283                    taskstatus("%s to %s", 
7284                        fileName.c_str(), toDirName.c_str());
7285                    String baseName = fileName;
7286                    unsigned int pos = baseName.find_last_of('/');
7287                    if (pos!=baseName.npos && pos<baseName.size()-1)
7288                        baseName = baseName.substr(pos+1, baseName.size());
7289                    String fullSource = parent.resolve(fileName);
7290                    String destPath;
7291                    if (toDirName.size()>0)
7292                        {
7293                        destPath.append(toDirName);
7294                        destPath.append("/");
7295                        }
7296                    destPath.append(baseName);
7297                    String fullDest = parent.resolve(destPath);
7298                    if (verbose)
7299                        taskstatus("file %s to new dir : %s", fullSource.c_str(),
7300                                           fullDest.c_str());
7301                    if (!isRegularFile(fullSource))
7302                        {
7303                        error("copy : file %s does not exist", fullSource.c_str());
7304                        return false;
7305                        }
7306                    if (!isNewerThan(fullSource, fullDest))
7307                        {
7308                        taskstatus("skipped");
7309                        return true;
7310                        }
7311                    if (!copyFile(fullSource, fullDest))
7312                        return false;
7313                    taskstatus("1 file copied");
7314                    }
7315                return true;
7316                }
7317            }
7318         return true;
7319         }
7322     virtual bool parse(Element *elem)
7323         {
7324         if (!parent.getAttribute(elem, "file", fileNameOpt))
7325             return false;
7326         if (!parent.getAttribute(elem, "tofile", toFileNameOpt))
7327             return false;
7328         if (toFileNameOpt.size() > 0)
7329             cptype = CP_TOFILE;
7330         if (!parent.getAttribute(elem, "todir", toDirNameOpt))
7331             return false;
7332         if (toDirNameOpt.size() > 0)
7333             cptype = CP_TODIR;
7334         if (!parent.getAttribute(elem, "verbose", verboseOpt))
7335             return false;
7336             
7337         haveFileSet = false;
7338         
7339         std::vector<Element *> children = elem->getChildren();
7340         for (unsigned int i=0 ; i<children.size() ; i++)
7341             {
7342             Element *child = children[i];
7343             String tagName = child->getName();
7344             if (tagName == "fileset")
7345                 {
7346                 if (!parseFileSet(child, parent, fileSet))
7347                     {
7348                     error("problem getting fileset");
7349                     return false;
7350                     }
7351                 haveFileSet = true;
7352                 }
7353             }
7355         //Perform validity checks
7356         if (fileNameOpt.size()>0 && fileSet.size()>0)
7357             {
7358             error("<copy> can only have one of : file= and <fileset>");
7359             return false;
7360             }
7361         if (toFileNameOpt.size()>0 && toDirNameOpt.size()>0)
7362             {
7363             error("<copy> can only have one of : tofile= or todir=");
7364             return false;
7365             }
7366         if (haveFileSet && toDirNameOpt.size()==0)
7367             {
7368             error("a <copy> task with a <fileset> must have : todir=");
7369             return false;
7370             }
7371         if (cptype == CP_TOFILE && fileNameOpt.size()==0)
7372             {
7373             error("<copy> tofile= must be associated with : file=");
7374             return false;
7375             }
7376         if (cptype == CP_TODIR && fileNameOpt.size()==0 && !haveFileSet)
7377             {
7378             error("<copy> todir= must be associated with : file= or <fileset>");
7379             return false;
7380             }
7382         return true;
7383         }
7384         
7385 private:
7387     int cptype;
7388     bool haveFileSet;
7390     FileSet fileSet;
7391     String  fileNameOpt;
7392     String  toFileNameOpt;
7393     String  toDirNameOpt;
7394     String  verboseOpt;
7395 };
7398 /**
7399  * Generate CxxTest files
7400  */
7401 class TaskCxxTestPart: public Task
7403 public:
7405     TaskCxxTestPart(MakeBase &par) : Task(par)
7406          {
7407          type    = TASK_CXXTEST_PART;
7408          name    = "cxxtestpart";
7409          }
7411     virtual ~TaskCxxTestPart()
7412         {}
7414     virtual bool execute()
7415         {
7416         if (!listFiles(parent, fileSet))
7417             return false;
7418         String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
7419                 
7420         String fullDest = parent.resolve(parent.eval(destPathOpt, "."));
7421         String cmd = parent.eval(commandOpt, "cxxtestgen.py");
7422         cmd.append(" --part -o ");
7423         cmd.append(fullDest);
7425         unsigned int newFiles = 0;
7426         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7427             {
7428             String fileName = fileSet[i];
7429             if (getSuffix(fileName) != "h")
7430                 continue;
7431             String sourcePath;
7432             if (fileSetDir.size()>0)
7433                 {
7434                 sourcePath.append(fileSetDir);
7435                 sourcePath.append("/");
7436                 }
7437             sourcePath.append(fileName);
7438             String fullSource = parent.resolve(sourcePath);
7440             cmd.append(" ");
7441             cmd.append(fullSource);
7442             if (isNewerThan(fullSource, fullDest)) newFiles++;
7443             }
7444         
7445         if (newFiles>0) {
7446             size_t const lastSlash = fullDest.find_last_of('/');
7447             if (lastSlash != fullDest.npos) {
7448                 String directory(fullDest, 0, lastSlash);
7449                 if (!createDirectory(directory))
7450                     return false;
7451             }
7453             String outString, errString;
7454             if (!executeCommand(cmd.c_str(), "", outString, errString))
7455                 {
7456                 error("<cxxtestpart> problem: %s", errString.c_str());
7457                 return false;
7458                 }
7459             removeFromStatCache(getNativePath(fullDest));
7460         }
7462         return true;
7463         }
7465     virtual bool parse(Element *elem)
7466         {
7467         if (!parent.getAttribute(elem, "command", commandOpt))
7468             return false;
7469         if (!parent.getAttribute(elem, "out", destPathOpt))
7470             return false;
7471             
7472         std::vector<Element *> children = elem->getChildren();
7473         for (unsigned int i=0 ; i<children.size() ; i++)
7474             {
7475             Element *child = children[i];
7476             String tagName = child->getName();
7477             if (tagName == "fileset")
7478                 {
7479                 if (!parseFileSet(child, parent, fileSet))
7480                     return false;
7481                 }
7482             }
7483         return true;
7484         }
7486 private:
7488     String  commandOpt;
7489     String  destPathOpt;
7490     FileSet fileSet;
7492 };
7495 /**
7496  * Generate the CxxTest root file
7497  */
7498 class TaskCxxTestRoot: public Task
7500 public:
7502     TaskCxxTestRoot(MakeBase &par) : Task(par)
7503          {
7504          type    = TASK_CXXTEST_ROOT;
7505          name    = "cxxtestroot";
7506          }
7508     virtual ~TaskCxxTestRoot()
7509         {}
7511     virtual bool execute()
7512         {
7513         if (!listFiles(parent, fileSet))
7514             return false;
7515         String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
7516         unsigned int newFiles = 0;
7517                 
7518         String fullDest = parent.resolve(parent.eval(destPathOpt, "."));
7519         String cmd = parent.eval(commandOpt, "cxxtestgen.py");
7520         cmd.append(" --root -o ");
7521         cmd.append(fullDest);
7522         String templateFile = parent.eval(templateFileOpt, "");
7523         if (templateFile.size()>0) {
7524             String fullTemplate = parent.resolve(templateFile);
7525             cmd.append(" --template=");
7526             cmd.append(fullTemplate);
7527             if (isNewerThan(fullTemplate, fullDest)) newFiles++;
7528         }
7530         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7531             {
7532             String fileName = fileSet[i];
7533             if (getSuffix(fileName) != "h")
7534                 continue;
7535             String sourcePath;
7536             if (fileSetDir.size()>0)
7537                 {
7538                 sourcePath.append(fileSetDir);
7539                 sourcePath.append("/");
7540                 }
7541             sourcePath.append(fileName);
7542             String fullSource = parent.resolve(sourcePath);
7544             cmd.append(" ");
7545             cmd.append(fullSource);
7546             if (isNewerThan(fullSource, fullDest)) newFiles++;
7547             }
7548         
7549         if (newFiles>0) {
7550             size_t const lastSlash = fullDest.find_last_of('/');
7551             if (lastSlash != fullDest.npos) {
7552                 String directory(fullDest, 0, lastSlash);
7553                 if (!createDirectory(directory))
7554                     return false;
7555             }
7557             String outString, errString;
7558             if (!executeCommand(cmd.c_str(), "", outString, errString))
7559                 {
7560                 error("<cxxtestroot> problem: %s", errString.c_str());
7561                 return false;
7562                 }
7563             removeFromStatCache(getNativePath(fullDest));
7564         }
7566         return true;
7567         }
7569     virtual bool parse(Element *elem)
7570         {
7571         if (!parent.getAttribute(elem, "command", commandOpt))
7572             return false;
7573         if (!parent.getAttribute(elem, "template", templateFileOpt))
7574             return false;
7575         if (!parent.getAttribute(elem, "out", destPathOpt))
7576             return false;
7577             
7578         std::vector<Element *> children = elem->getChildren();
7579         for (unsigned int i=0 ; i<children.size() ; i++)
7580             {
7581             Element *child = children[i];
7582             String tagName = child->getName();
7583             if (tagName == "fileset")
7584                 {
7585                 if (!parseFileSet(child, parent, fileSet))
7586                     return false;
7587                 }
7588             }
7589         return true;
7590         }
7592 private:
7594     String  commandOpt;
7595     String  templateFileOpt;
7596     String  destPathOpt;
7597     FileSet fileSet;
7599 };
7602 /**
7603  * Execute the CxxTest test executable
7604  */
7605 class TaskCxxTestRun: public Task
7607 public:
7609     TaskCxxTestRun(MakeBase &par) : Task(par)
7610          {
7611          type    = TASK_CXXTEST_RUN;
7612          name    = "cxxtestrun";
7613          }
7615     virtual ~TaskCxxTestRun()
7616         {}
7618     virtual bool execute()
7619         {
7620         unsigned int newFiles = 0;
7621                 
7622         String workingDir = parent.resolve(parent.eval(workingDirOpt, "inkscape"));
7623         String rawCmd = parent.eval(commandOpt, "build/cxxtests");
7625         String cmdExe;
7626         if (fileExists(rawCmd)) {
7627             cmdExe = rawCmd;
7628         } else if (fileExists(rawCmd + ".exe")) {
7629             cmdExe = rawCmd + ".exe";
7630         } else {
7631             error("<cxxtestrun> problem: cxxtests executable not found! (command=\"%s\")", rawCmd.c_str());
7632         }
7633         // Note that the log file names are based on the exact name used to call cxxtests (it uses argv[0] + ".log"/".xml")
7634         if (isNewerThan(cmdExe, rawCmd + ".log") || isNewerThan(cmdExe, rawCmd + ".xml")) newFiles++;
7636         // Prepend the necessary ../'s
7637         String cmd = rawCmd;
7638         unsigned int workingDirDepth = 0;
7639         bool wasSlash = true;
7640         for(size_t i=0; i<workingDir.size(); i++) {
7641             // This assumes no . and .. parts
7642             if (wasSlash && workingDir[i]!='/') workingDirDepth++;
7643             wasSlash = workingDir[i] == '/';
7644         }
7645         for(size_t i=0; i<workingDirDepth; i++) {
7646             cmd = "../" + cmd;
7647         }
7648         
7649         if (newFiles>0) {
7650             char olddir[1024];
7651             if (workingDir.size()>0) {
7652                 // TODO: Double-check usage of getcwd and handle chdir errors
7653                 getcwd(olddir, 1024);
7654                 chdir(workingDir.c_str());
7655             }
7657             String outString;
7658             if (!executeCommand(cmd.c_str(), "", outString, outString))
7659                 {
7660                 error("<cxxtestrun> problem: %s", outString.c_str());
7661                 return false;
7662                 }
7664             if (workingDir.size()>0) {
7665                 // TODO: Handle errors?
7666                 chdir(olddir);
7667             }
7669             removeFromStatCache(getNativePath(cmd + ".log"));
7670             removeFromStatCache(getNativePath(cmd + ".xml"));
7671         }
7673         return true;
7674         }
7676     virtual bool parse(Element *elem)
7677         {
7678         if (!parent.getAttribute(elem, "command", commandOpt))
7679             return false;
7680         if (!parent.getAttribute(elem, "workingdir", workingDirOpt))
7681             return false;
7682         return true;
7683         }
7685 private:
7687     String  commandOpt;
7688     String  workingDirOpt;
7690 };
7693 /**
7694  *
7695  */
7696 class TaskDelete : public Task
7698 public:
7700     typedef enum
7701         {
7702         DEL_FILE,
7703         DEL_DIR,
7704         DEL_FILESET
7705         } DeleteType;
7707     TaskDelete(MakeBase &par) : Task(par)
7708         { 
7709         type        = TASK_DELETE;
7710         name        = "delete";
7711         delType     = DEL_FILE;
7712         }
7714     virtual ~TaskDelete()
7715         {}
7717     virtual bool execute()
7718         {
7719         String dirName   = parent.eval(dirNameOpt, ".");
7720         String fileName  = parent.eval(fileNameOpt, ".");
7721         bool verbose     = parent.evalBool(verboseOpt, false);
7722         bool quiet       = parent.evalBool(quietOpt, false);
7723         bool failOnError = parent.evalBool(failOnErrorOpt, true);
7724         switch (delType)
7725             {
7726             case DEL_FILE:
7727                 {
7728                 taskstatus("file: %s", fileName.c_str());
7729                 String fullName = parent.resolve(fileName);
7730                 char *fname = (char *)fullName.c_str();
7731                 if (!quiet && verbose)
7732                     taskstatus("path: %s", fname);
7733                 if (failOnError && !removeFile(fullName))
7734                     {
7735                     //error("Could not delete file '%s'", fullName.c_str());
7736                     return false;
7737                     }
7738                 return true;
7739                 }
7740             case DEL_DIR:
7741                 {
7742                 taskstatus("dir: %s", dirName.c_str());
7743                 String fullDir = parent.resolve(dirName);
7744                 if (!quiet && verbose)
7745                     taskstatus("path: %s", fullDir.c_str());
7746                 if (failOnError && !removeDirectory(fullDir))
7747                     {
7748                     //error("Could not delete directory '%s'", fullDir.c_str());
7749                     return false;
7750                     }
7751                 return true;
7752                 }
7753             }
7754         return true;
7755         }
7757     virtual bool parse(Element *elem)
7758         {
7759         if (!parent.getAttribute(elem, "file", fileNameOpt))
7760             return false;
7761         if (fileNameOpt.size() > 0)
7762             delType = DEL_FILE;
7763         if (!parent.getAttribute(elem, "dir", dirNameOpt))
7764             return false;
7765         if (dirNameOpt.size() > 0)
7766             delType = DEL_DIR;
7767         if (fileNameOpt.size()>0 && dirNameOpt.size()>0)
7768             {
7769             error("<delete> can have one attribute of file= or dir=");
7770             return false;
7771             }
7772         if (fileNameOpt.size()==0 && dirNameOpt.size()==0)
7773             {
7774             error("<delete> must have one attribute of file= or dir=");
7775             return false;
7776             }
7777         if (!parent.getAttribute(elem, "verbose", verboseOpt))
7778             return false;
7779         if (!parent.getAttribute(elem, "quiet", quietOpt))
7780             return false;
7781         if (!parent.getAttribute(elem, "failonerror", failOnErrorOpt))
7782             return false;
7783         return true;
7784         }
7786 private:
7788     int delType;
7789     String dirNameOpt;
7790     String fileNameOpt;
7791     String verboseOpt;
7792     String quietOpt;
7793     String failOnErrorOpt;
7794 };
7797 /**
7798  * Send a message to stdout
7799  */
7800 class TaskEcho : public Task
7802 public:
7804     TaskEcho(MakeBase &par) : Task(par)
7805         { type = TASK_ECHO; name = "echo"; }
7807     virtual ~TaskEcho()
7808         {}
7810     virtual bool execute()
7811         {
7812         //let message have priority over text
7813         String message = parent.eval(messageOpt, "");
7814         String text    = parent.eval(textOpt, "");
7815         if (message.size() > 0)
7816             {
7817             fprintf(stdout, "%s\n", message.c_str());
7818             }
7819         else if (text.size() > 0)
7820             {
7821             fprintf(stdout, "%s\n", text.c_str());
7822             }
7823         return true;
7824         }
7826     virtual bool parse(Element *elem)
7827         {
7828         if (!parent.getValue(elem, textOpt))
7829             return false;
7830         textOpt    = leftJustify(textOpt);
7831         if (!parent.getAttribute(elem, "message", messageOpt))
7832             return false;
7833         return true;
7834         }
7836 private:
7838     String messageOpt;
7839     String textOpt;
7840 };
7844 /**
7845  *
7846  */
7847 class TaskJar : public Task
7849 public:
7851     TaskJar(MakeBase &par) : Task(par)
7852         { type = TASK_JAR; name = "jar"; }
7854     virtual ~TaskJar()
7855         {}
7857     virtual bool execute()
7858         {
7859         String command  = parent.eval(commandOpt, "jar");
7860         String basedir  = parent.eval(basedirOpt, ".");
7861         String destfile = parent.eval(destfileOpt, ".");
7863         String cmd = command;
7864         cmd.append(" -cf ");
7865         cmd.append(destfile);
7866         cmd.append(" -C ");
7867         cmd.append(basedir);
7868         cmd.append(" .");
7870         String execCmd = cmd;
7872         String outString, errString;
7873         bool ret = executeCommand(execCmd.c_str(), "", outString, errString);
7874         if (!ret)
7875             {
7876             error("<jar> command '%s' failed :\n %s",
7877                                       execCmd.c_str(), errString.c_str());
7878             return false;
7879             }
7880         removeFromStatCache(getNativePath(destfile));
7881         return true;
7882         }
7884     virtual bool parse(Element *elem)
7885         {
7886         if (!parent.getAttribute(elem, "command", commandOpt))
7887             return false;
7888         if (!parent.getAttribute(elem, "basedir", basedirOpt))
7889             return false;
7890         if (!parent.getAttribute(elem, "destfile", destfileOpt))
7891             return false;
7892         if (basedirOpt.size() == 0 || destfileOpt.size() == 0)
7893             {
7894             error("<jar> required both basedir and destfile attributes to be set");
7895             return false;
7896             }
7897         return true;
7898         }
7900 private:
7902     String commandOpt;
7903     String basedirOpt;
7904     String destfileOpt;
7905 };
7908 /**
7909  *
7910  */
7911 class TaskJavac : public Task
7913 public:
7915     TaskJavac(MakeBase &par) : Task(par)
7916         { 
7917         type = TASK_JAVAC; name = "javac";
7918         }
7920     virtual ~TaskJavac()
7921         {}
7923     virtual bool execute()
7924         {
7925         String command  = parent.eval(commandOpt, "javac");
7926         String srcdir   = parent.eval(srcdirOpt, ".");
7927         String destdir  = parent.eval(destdirOpt, ".");
7928         String target   = parent.eval(targetOpt, "");
7930         std::vector<String> fileList;
7931         if (!listFiles(srcdir, "", fileList))
7932             {
7933             return false;
7934             }
7935         String cmd = command;
7936         cmd.append(" -d ");
7937         cmd.append(destdir);
7938         cmd.append(" -classpath ");
7939         cmd.append(destdir);
7940         cmd.append(" -sourcepath ");
7941         cmd.append(srcdir);
7942         cmd.append(" ");
7943         if (target.size()>0)
7944             {
7945             cmd.append(" -target ");
7946             cmd.append(target);
7947             cmd.append(" ");
7948             }
7949         String fname = "javalist.btool";
7950         FILE *f = fopen(fname.c_str(), "w");
7951         int count = 0;
7952         for (unsigned int i=0 ; i<fileList.size() ; i++)
7953             {
7954             String fname = fileList[i];
7955             String srcName = fname;
7956             if (fname.size()<6) //x.java
7957                 continue;
7958             if (fname.compare(fname.size()-5, 5, ".java") != 0)
7959                 continue;
7960             String baseName = fname.substr(0, fname.size()-5);
7961             String destName = baseName;
7962             destName.append(".class");
7964             String fullSrc = srcdir;
7965             fullSrc.append("/");
7966             fullSrc.append(fname);
7967             String fullDest = destdir;
7968             fullDest.append("/");
7969             fullDest.append(destName);
7970             //trace("fullsrc:%s fulldest:%s", fullSrc.c_str(), fullDest.c_str());
7971             if (!isNewerThan(fullSrc, fullDest))
7972                 continue;
7974             count++;
7975             fprintf(f, "%s\n", fullSrc.c_str());
7976             }
7977         fclose(f);
7978         if (!count)
7979             {
7980             taskstatus("nothing to do");
7981             return true;
7982             }
7984         taskstatus("compiling %d files", count);
7986         String execCmd = cmd;
7987         execCmd.append("@");
7988         execCmd.append(fname);
7990         String outString, errString;
7991         bool ret = executeCommand(execCmd.c_str(), "", outString, errString);
7992         if (!ret)
7993             {
7994             error("<javac> command '%s' failed :\n %s",
7995                                       execCmd.c_str(), errString.c_str());
7996             return false;
7997             }
7998         // TODO: 
7999         //removeFromStatCache(getNativePath(........));
8000         return true;
8001         }
8003     virtual bool parse(Element *elem)
8004         {
8005         if (!parent.getAttribute(elem, "command", commandOpt))
8006             return false;
8007         if (!parent.getAttribute(elem, "srcdir", srcdirOpt))
8008             return false;
8009         if (!parent.getAttribute(elem, "destdir", destdirOpt))
8010             return false;
8011         if (srcdirOpt.size() == 0 || destdirOpt.size() == 0)
8012             {
8013             error("<javac> required both srcdir and destdir attributes to be set");
8014             return false;
8015             }
8016         if (!parent.getAttribute(elem, "target", targetOpt))
8017             return false;
8018         return true;
8019         }
8021 private:
8023     String commandOpt;
8024     String srcdirOpt;
8025     String destdirOpt;
8026     String targetOpt;
8028 };
8031 /**
8032  *
8033  */
8034 class TaskLink : public Task
8036 public:
8038     TaskLink(MakeBase &par) : Task(par)
8039         {
8040         type = TASK_LINK; name = "link";
8041         }
8043     virtual ~TaskLink()
8044         {}
8046     virtual bool execute()
8047         {
8048         String  command        = parent.eval(commandOpt, "g++");
8049         String  fileName       = parent.eval(fileNameOpt, "");
8050         String  flags          = parent.eval(flagsOpt, "");
8051         String  libs           = parent.eval(libsOpt, "");
8052         bool    doStrip        = parent.evalBool(doStripOpt, false);
8053         String  symFileName    = parent.eval(symFileNameOpt, "");
8054         String  stripCommand   = parent.eval(stripCommandOpt, "strip");
8055         String  objcopyCommand = parent.eval(objcopyCommandOpt, "objcopy");
8057         if (!listFiles(parent, fileSet))
8058             return false;
8059         String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
8060         //trace("%d files in %s", fileSet.size(), fileSetDir.c_str());
8061         bool doit = false;
8062         String fullTarget = parent.resolve(fileName);
8063         String cmd = command;
8064         cmd.append(" -o ");
8065         cmd.append(fullTarget);
8066         cmd.append(" ");
8067         cmd.append(flags);
8068         for (unsigned int i=0 ; i<fileSet.size() ; i++)
8069             {
8070             cmd.append(" ");
8071             String obj;
8072             if (fileSetDir.size()>0)
8073                 {
8074                 obj.append(fileSetDir);
8075                 obj.append("/");
8076                 }
8077             obj.append(fileSet[i]);
8078             String fullObj = parent.resolve(obj);
8079             String nativeFullObj = getNativePath(fullObj);
8080             cmd.append(nativeFullObj);
8081             //trace("link: tgt:%s obj:%s", fullTarget.c_str(),
8082             //          fullObj.c_str());
8083             if (isNewerThan(fullObj, fullTarget))
8084                 doit = true;
8085             }
8086         cmd.append(" ");
8087         cmd.append(libs);
8088         if (!doit)
8089             {
8090             //trace("link not needed");
8091             return true;
8092             }
8093         //trace("LINK cmd:%s", cmd.c_str());
8096         String outbuf, errbuf;
8097         if (!executeCommand(cmd.c_str(), "", outbuf, errbuf))
8098             {
8099             error("LINK problem: %s", errbuf.c_str());
8100             return false;
8101             }
8102         removeFromStatCache(getNativePath(fullTarget));
8104         if (symFileName.size()>0)
8105             {
8106             String symFullName = parent.resolve(symFileName);
8107             cmd = objcopyCommand;
8108             cmd.append(" --only-keep-debug ");
8109             cmd.append(getNativePath(fullTarget));
8110             cmd.append(" ");
8111             cmd.append(getNativePath(symFullName));
8112             if (!executeCommand(cmd, "", outbuf, errbuf))
8113                 {
8114                 error("<strip> symbol file failed : %s", errbuf.c_str());
8115                 return false;
8116                 }
8117             removeFromStatCache(getNativePath(symFullName));
8118             }
8119             
8120         if (doStrip)
8121             {
8122             cmd = stripCommand;
8123             cmd.append(" ");
8124             cmd.append(getNativePath(fullTarget));
8125             if (!executeCommand(cmd, "", outbuf, errbuf))
8126                {
8127                error("<strip> failed : %s", errbuf.c_str());
8128                return false;
8129                }
8130             removeFromStatCache(getNativePath(fullTarget));
8131             }
8133         return true;
8134         }
8136     virtual bool parse(Element *elem)
8137         {
8138         if (!parent.getAttribute(elem, "command", commandOpt))
8139             return false;
8140         if (!parent.getAttribute(elem, "objcopycommand", objcopyCommandOpt))
8141             return false;
8142         if (!parent.getAttribute(elem, "stripcommand", stripCommandOpt))
8143             return false;
8144         if (!parent.getAttribute(elem, "out", fileNameOpt))
8145             return false;
8146         if (!parent.getAttribute(elem, "strip", doStripOpt))
8147             return false;
8148         if (!parent.getAttribute(elem, "symfile", symFileNameOpt))
8149             return false;
8150             
8151         std::vector<Element *> children = elem->getChildren();
8152         for (unsigned int i=0 ; i<children.size() ; i++)
8153             {
8154             Element *child = children[i];
8155             String tagName = child->getName();
8156             if (tagName == "fileset")
8157                 {
8158                 if (!parseFileSet(child, parent, fileSet))
8159                     return false;
8160                 }
8161             else if (tagName == "flags")
8162                 {
8163                 if (!parent.getValue(child, flagsOpt))
8164                     return false;
8165                 flagsOpt = strip(flagsOpt);
8166                 }
8167             else if (tagName == "libs")
8168                 {
8169                 if (!parent.getValue(child, libsOpt))
8170                     return false;
8171                 libsOpt = strip(libsOpt);
8172                 }
8173             }
8174         return true;
8175         }
8177 private:
8179     FileSet fileSet;
8181     String  commandOpt;
8182     String  fileNameOpt;
8183     String  flagsOpt;
8184     String  libsOpt;
8185     String  doStripOpt;
8186     String  symFileNameOpt;
8187     String  stripCommandOpt;
8188     String  objcopyCommandOpt;
8190 };
8194 /**
8195  * Create a named file
8196  */
8197 class TaskMakeFile : public Task
8199 public:
8201     TaskMakeFile(MakeBase &par) : Task(par)
8202         { type = TASK_MAKEFILE; name = "makefile"; }
8204     virtual ~TaskMakeFile()
8205         {}
8207     virtual bool execute()
8208         {
8209         String fileName = parent.eval(fileNameOpt, "");
8210         bool force      = parent.evalBool(forceOpt, false);
8211         String text     = parent.eval(textOpt, "");
8213         taskstatus("%s", fileName.c_str());
8214         String fullName = parent.resolve(fileName);
8215         if (!force && !isNewerThan(parent.getURI().getPath(), fullName))
8216             {
8217             taskstatus("skipped");
8218             return true;
8219             }
8220         String fullNative = getNativePath(fullName);
8221         //trace("fullName:%s", fullName.c_str());
8222         FILE *f = fopen(fullNative.c_str(), "w");
8223         if (!f)
8224             {
8225             error("<makefile> could not open %s for writing : %s",
8226                 fullName.c_str(), strerror(errno));
8227             return false;
8228             }
8229         for (unsigned int i=0 ; i<text.size() ; i++)
8230             fputc(text[i], f);
8231         fputc('\n', f);
8232         fclose(f);
8233         removeFromStatCache(fullNative);
8234         return true;
8235         }
8237     virtual bool parse(Element *elem)
8238         {
8239         if (!parent.getAttribute(elem, "file", fileNameOpt))
8240             return false;
8241         if (!parent.getAttribute(elem, "force", forceOpt))
8242             return false;
8243         if (fileNameOpt.size() == 0)
8244             {
8245             error("<makefile> requires 'file=\"filename\"' attribute");
8246             return false;
8247             }
8248         if (!parent.getValue(elem, textOpt))
8249             return false;
8250         textOpt = leftJustify(textOpt);
8251         //trace("dirname:%s", dirName.c_str());
8252         return true;
8253         }
8255 private:
8257     String fileNameOpt;
8258     String forceOpt;
8259     String textOpt;
8260 };
8264 /**
8265  * Create a named directory
8266  */
8267 class TaskMkDir : public Task
8269 public:
8271     TaskMkDir(MakeBase &par) : Task(par)
8272         { type = TASK_MKDIR; name = "mkdir"; }
8274     virtual ~TaskMkDir()
8275         {}
8277     virtual bool execute()
8278         {
8279         String dirName = parent.eval(dirNameOpt, ".");
8280         
8281         taskstatus("%s", dirName.c_str());
8282         String fullDir = parent.resolve(dirName);
8283         //trace("fullDir:%s", fullDir.c_str());
8284         if (!createDirectory(fullDir))
8285             return false;
8286         return true;
8287         }
8289     virtual bool parse(Element *elem)
8290         {
8291         if (!parent.getAttribute(elem, "dir", dirNameOpt))
8292             return false;
8293         if (dirNameOpt.size() == 0)
8294             {
8295             error("<mkdir> requires 'dir=\"dirname\"' attribute");
8296             return false;
8297             }
8298         return true;
8299         }
8301 private:
8303     String dirNameOpt;
8304 };
8308 /**
8309  * Create a named directory
8310  */
8311 class TaskMsgFmt: public Task
8313 public:
8315     TaskMsgFmt(MakeBase &par) : Task(par)
8316          { type = TASK_MSGFMT;  name = "msgfmt"; }
8318     virtual ~TaskMsgFmt()
8319         {}
8321     virtual bool execute()
8322         {
8323         String  command   = parent.eval(commandOpt, "msgfmt");
8324         String  toDirName = parent.eval(toDirNameOpt, ".");
8325         String  outName   = parent.eval(outNameOpt, "");
8326         bool    owndir    = parent.evalBool(owndirOpt, false);
8328         if (!listFiles(parent, fileSet))
8329             return false;
8330         String fileSetDir = fileSet.getDirectory();
8332         //trace("msgfmt: %d", fileSet.size());
8333         for (unsigned int i=0 ; i<fileSet.size() ; i++)
8334             {
8335             String fileName = fileSet[i];
8336             if (getSuffix(fileName) != "po")
8337                 continue;
8338             String sourcePath;
8339             if (fileSetDir.size()>0)
8340                 {
8341                 sourcePath.append(fileSetDir);
8342                 sourcePath.append("/");
8343                 }
8344             sourcePath.append(fileName);
8345             String fullSource = parent.resolve(sourcePath);
8347             String destPath;
8348             if (toDirName.size()>0)
8349                 {
8350                 destPath.append(toDirName);
8351                 destPath.append("/");
8352                 }
8353             if (owndir)
8354                 {
8355                 String subdir = fileName;
8356                 unsigned int pos = subdir.find_last_of('.');
8357                 if (pos != subdir.npos)
8358                     subdir = subdir.substr(0, pos);
8359                 destPath.append(subdir);
8360                 destPath.append("/");
8361                 }
8362             //Pick the output file name
8363             if (outName.size() > 0)
8364                 {
8365                 destPath.append(outName);
8366                 }
8367             else
8368                 {
8369                 destPath.append(fileName);
8370                 destPath[destPath.size()-2] = 'm';
8371                 }
8373             String fullDest = parent.resolve(destPath);
8375             if (!isNewerThan(fullSource, fullDest))
8376                 {
8377                 //trace("skip %s", fullSource.c_str());
8378                 continue;
8379                 }
8380                 
8381             String cmd = command;
8382             cmd.append(" ");
8383             cmd.append(fullSource);
8384             cmd.append(" -o ");
8385             cmd.append(fullDest);
8386             
8387             int pos = fullDest.find_last_of('/');
8388             if (pos>0)
8389                 {
8390                 String fullDestPath = fullDest.substr(0, pos);
8391                 if (!createDirectory(fullDestPath))
8392                     return false;
8393                 }
8397             String outString, errString;
8398             if (!executeCommand(cmd.c_str(), "", outString, errString))
8399                 {
8400                 error("<msgfmt> problem: %s", errString.c_str());
8401                 return false;
8402                 }
8403             removeFromStatCache(getNativePath(fullDest));
8404             }
8406         return true;
8407         }
8409     virtual bool parse(Element *elem)
8410         {
8411         if (!parent.getAttribute(elem, "command", commandOpt))
8412             return false;
8413         if (!parent.getAttribute(elem, "todir", toDirNameOpt))
8414             return false;
8415         if (!parent.getAttribute(elem, "out", outNameOpt))
8416             return false;
8417         if (!parent.getAttribute(elem, "owndir", owndirOpt))
8418             return false;
8419             
8420         std::vector<Element *> children = elem->getChildren();
8421         for (unsigned int i=0 ; i<children.size() ; i++)
8422             {
8423             Element *child = children[i];
8424             String tagName = child->getName();
8425             if (tagName == "fileset")
8426                 {
8427                 if (!parseFileSet(child, parent, fileSet))
8428                     return false;
8429                 }
8430             }
8431         return true;
8432         }
8434 private:
8436     FileSet fileSet;
8438     String  commandOpt;
8439     String  toDirNameOpt;
8440     String  outNameOpt;
8441     String  owndirOpt;
8443 };
8447 /**
8448  *  Perform a Package-Config query similar to pkg-config
8449  */
8450 class TaskPkgConfig : public Task
8452 public:
8454     typedef enum
8455         {
8456         PKG_CONFIG_QUERY_CFLAGS,
8457         PKG_CONFIG_QUERY_LIBS,
8458         PKG_CONFIG_QUERY_ALL
8459         } QueryTypes;
8461     TaskPkgConfig(MakeBase &par) : Task(par)
8462         {
8463         type = TASK_PKG_CONFIG;
8464         name = "pkg-config";
8465         }
8467     virtual ~TaskPkgConfig()
8468         {}
8470     virtual bool execute()
8471         {
8472         String pkgName       = parent.eval(pkgNameOpt,      "");
8473         String prefix        = parent.eval(prefixOpt,       "");
8474         String propName      = parent.eval(propNameOpt,     "");
8475         String pkgConfigPath = parent.eval(pkgConfigPathOpt,"");
8476         String query         = parent.eval(queryOpt,        "all");
8478         String path = parent.resolve(pkgConfigPath);
8479         PkgConfig pkgconfig;
8480         pkgconfig.setPath(path);
8481         pkgconfig.setPrefix(prefix);
8482         if (!pkgconfig.query(pkgName))
8483             {
8484             error("<pkg-config> query failed for '%s", name.c_str());
8485             return false;
8486             }
8487             
8488         String val = "";
8489         if (query == "cflags")
8490             val = pkgconfig.getCflags();
8491         else if (query == "libs")
8492             val =pkgconfig.getLibs();
8493         else if (query == "all")
8494             val = pkgconfig.getAll();
8495         else
8496             {
8497             error("<pkg-config> unhandled query : %s", query.c_str());
8498             return false;
8499             }
8500         taskstatus("property %s = '%s'", propName.c_str(), val.c_str());
8501         parent.setProperty(propName, val);
8502         return true;
8503         }
8505     virtual bool parse(Element *elem)
8506         {
8507         //# NAME
8508         if (!parent.getAttribute(elem, "name", pkgNameOpt))
8509             return false;
8510         if (pkgNameOpt.size()==0)
8511             {
8512             error("<pkg-config> requires 'name=\"package\"' attribute");
8513             return false;
8514             }
8516         //# PROPERTY
8517         if (!parent.getAttribute(elem, "property", propNameOpt))
8518             return false;
8519         if (propNameOpt.size()==0)
8520             {
8521             error("<pkg-config> requires 'property=\"name\"' attribute");
8522             return false;
8523             }
8524         //# PATH
8525         if (!parent.getAttribute(elem, "path", pkgConfigPathOpt))
8526             return false;
8527         //# PREFIX
8528         if (!parent.getAttribute(elem, "prefix", prefixOpt))
8529             return false;
8530         //# QUERY
8531         if (!parent.getAttribute(elem, "query", queryOpt))
8532             return false;
8534         return true;
8535         }
8537 private:
8539     String queryOpt;
8540     String pkgNameOpt;
8541     String prefixOpt;
8542     String propNameOpt;
8543     String pkgConfigPathOpt;
8545 };
8552 /**
8553  *  Process an archive to allow random access
8554  */
8555 class TaskRanlib : public Task
8557 public:
8559     TaskRanlib(MakeBase &par) : Task(par)
8560         { type = TASK_RANLIB; name = "ranlib"; }
8562     virtual ~TaskRanlib()
8563         {}
8565     virtual bool execute()
8566         {
8567         String fileName = parent.eval(fileNameOpt, "");
8568         String command  = parent.eval(commandOpt, "ranlib");
8570         String fullName = parent.resolve(fileName);
8571         //trace("fullDir:%s", fullDir.c_str());
8572         String cmd = command;
8573         cmd.append(" ");
8574         cmd.append(fullName);
8575         String outbuf, errbuf;
8576         if (!executeCommand(cmd, "", outbuf, errbuf))
8577             return false;
8578         // TODO:
8579         //removeFromStatCache(getNativePath(fullDest));
8580         return true;
8581         }
8583     virtual bool parse(Element *elem)
8584         {
8585         if (!parent.getAttribute(elem, "command", commandOpt))
8586             return false;
8587         if (!parent.getAttribute(elem, "file", fileNameOpt))
8588             return false;
8589         if (fileNameOpt.size() == 0)
8590             {
8591             error("<ranlib> requires 'file=\"fileNname\"' attribute");
8592             return false;
8593             }
8594         return true;
8595         }
8597 private:
8599     String fileNameOpt;
8600     String commandOpt;
8601 };
8605 /**
8606  * Compile a resource file into a binary object
8607  */
8608 class TaskRC : public Task
8610 public:
8612     TaskRC(MakeBase &par) : Task(par)
8613         { type = TASK_RC; name = "rc"; }
8615     virtual ~TaskRC()
8616         {}
8618     virtual bool execute()
8619         {
8620         String command  = parent.eval(commandOpt,  "windres");
8621         String flags    = parent.eval(flagsOpt,    "");
8622         String fileName = parent.eval(fileNameOpt, "");
8623         String outName  = parent.eval(outNameOpt,  "");
8625         String fullFile = parent.resolve(fileName);
8626         String fullOut  = parent.resolve(outName);
8627         if (!isNewerThan(fullFile, fullOut))
8628             return true;
8629         String cmd = command;
8630         cmd.append(" -o ");
8631         cmd.append(fullOut);
8632         cmd.append(" ");
8633         cmd.append(flags);
8634         cmd.append(" ");
8635         cmd.append(fullFile);
8637         String outString, errString;
8638         if (!executeCommand(cmd.c_str(), "", outString, errString))
8639             {
8640             error("RC problem: %s", errString.c_str());
8641             return false;
8642             }
8643         removeFromStatCache(getNativePath(fullOut));
8644         return true;
8645         }
8647     virtual bool parse(Element *elem)
8648         {
8649         if (!parent.getAttribute(elem, "command", commandOpt))
8650             return false;
8651         if (!parent.getAttribute(elem, "file", fileNameOpt))
8652             return false;
8653         if (!parent.getAttribute(elem, "out", outNameOpt))
8654             return false;
8655         std::vector<Element *> children = elem->getChildren();
8656         for (unsigned int i=0 ; i<children.size() ; i++)
8657             {
8658             Element *child = children[i];
8659             String tagName = child->getName();
8660             if (tagName == "flags")
8661                 {
8662                 if (!parent.getValue(child, flagsOpt))
8663                     return false;
8664                 }
8665             }
8666         return true;
8667         }
8669 private:
8671     String commandOpt;
8672     String flagsOpt;
8673     String fileNameOpt;
8674     String outNameOpt;
8676 };
8680 /**
8681  *  Collect .o's into a .so or DLL
8682  */
8683 class TaskSharedLib : public Task
8685 public:
8687     TaskSharedLib(MakeBase &par) : Task(par)
8688         { type = TASK_SHAREDLIB; name = "dll"; }
8690     virtual ~TaskSharedLib()
8691         {}
8693     virtual bool execute()
8694         {
8695         String command     = parent.eval(commandOpt, "dllwrap");
8696         String fileName    = parent.eval(fileNameOpt, "");
8697         String defFileName = parent.eval(defFileNameOpt, "");
8698         String impFileName = parent.eval(impFileNameOpt, "");
8699         String libs        = parent.eval(libsOpt, "");
8701         //trace("###########HERE %d", fileSet.size());
8702         bool doit = false;
8703         
8704         String fullOut = parent.resolve(fileName);
8705         //trace("ar fullout: %s", fullOut.c_str());
8706         
8707         if (!listFiles(parent, fileSet))
8708             return false;
8709         String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
8711         for (unsigned int i=0 ; i<fileSet.size() ; i++)
8712             {
8713             String fname;
8714             if (fileSetDir.size()>0)
8715                 {
8716                 fname.append(fileSetDir);
8717                 fname.append("/");
8718                 }
8719             fname.append(fileSet[i]);
8720             String fullName = parent.resolve(fname);
8721             //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
8722             if (isNewerThan(fullName, fullOut))
8723                 doit = true;
8724             }
8725         //trace("Needs it:%d", doit);
8726         if (!doit)
8727             {
8728             return true;
8729             }
8731         String cmd = "dllwrap";
8732         cmd.append(" -o ");
8733         cmd.append(fullOut);
8734         if (defFileName.size()>0)
8735             {
8736             cmd.append(" --def ");
8737             cmd.append(defFileName);
8738             cmd.append(" ");
8739             }
8740         if (impFileName.size()>0)
8741             {
8742             cmd.append(" --implib ");
8743             cmd.append(impFileName);
8744             cmd.append(" ");
8745             }
8746         for (unsigned int i=0 ; i<fileSet.size() ; i++)
8747             {
8748             String fname;
8749             if (fileSetDir.size()>0)
8750                 {
8751                 fname.append(fileSetDir);
8752                 fname.append("/");
8753                 }
8754             fname.append(fileSet[i]);
8755             String fullName = parent.resolve(fname);
8757             cmd.append(" ");
8758             cmd.append(fullName);
8759             }
8760         cmd.append(" ");
8761         cmd.append(libs);
8763         String outString, errString;
8764         if (!executeCommand(cmd.c_str(), "", outString, errString))
8765             {
8766             error("<sharedlib> problem: %s", errString.c_str());
8767             return false;
8768             }
8769         removeFromStatCache(getNativePath(fullOut));
8770         return true;
8771         }
8773     virtual bool parse(Element *elem)
8774         {
8775         if (!parent.getAttribute(elem, "command", commandOpt))
8776             return false;
8777         if (!parent.getAttribute(elem, "file", fileNameOpt))
8778             return false;
8779         if (!parent.getAttribute(elem, "import", impFileNameOpt))
8780             return false;
8781         if (!parent.getAttribute(elem, "def", defFileNameOpt))
8782             return false;
8783             
8784         std::vector<Element *> children = elem->getChildren();
8785         for (unsigned int i=0 ; i<children.size() ; i++)
8786             {
8787             Element *child = children[i];
8788             String tagName = child->getName();
8789             if (tagName == "fileset")
8790                 {
8791                 if (!parseFileSet(child, parent, fileSet))
8792                     return false;
8793                 }
8794             else if (tagName == "libs")
8795                 {
8796                 if (!parent.getValue(child, libsOpt))
8797                     return false;
8798                 libsOpt = strip(libsOpt);
8799                 }
8800             }
8801         return true;
8802         }
8804 private:
8806     FileSet fileSet;
8808     String commandOpt;
8809     String fileNameOpt;
8810     String defFileNameOpt;
8811     String impFileNameOpt;
8812     String libsOpt;
8814 };
8818 /**
8819  * Run the "ar" command to archive .o's into a .a
8820  */
8821 class TaskStaticLib : public Task
8823 public:
8825     TaskStaticLib(MakeBase &par) : Task(par)
8826         { type = TASK_STATICLIB; name = "staticlib"; }
8828     virtual ~TaskStaticLib()
8829         {}
8831     virtual bool execute()
8832         {
8833         String command = parent.eval(commandOpt, "ar crv");
8834         String fileName = parent.eval(fileNameOpt, "");
8836         bool doit = false;
8837         
8838         String fullOut = parent.resolve(fileName);
8839         //trace("ar fullout: %s", fullOut.c_str());
8840         
8841         if (!listFiles(parent, fileSet))
8842             return false;
8843         String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
8844         //trace("###########HERE %s", fileSetDir.c_str());
8846         for (unsigned int i=0 ; i<fileSet.size() ; i++)
8847             {
8848             String fname;
8849             if (fileSetDir.size()>0)
8850                 {
8851                 fname.append(fileSetDir);
8852                 fname.append("/");
8853                 }
8854             fname.append(fileSet[i]);
8855             String fullName = parent.resolve(fname);
8856             //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
8857             if (isNewerThan(fullName, fullOut))
8858                 doit = true;
8859             }
8860         //trace("Needs it:%d", doit);
8861         if (!doit)
8862             {
8863             return true;
8864             }
8866         String cmd = command;
8867         cmd.append(" ");
8868         cmd.append(fullOut);
8869         for (unsigned int i=0 ; i<fileSet.size() ; i++)
8870             {
8871             String fname;
8872             if (fileSetDir.size()>0)
8873                 {
8874                 fname.append(fileSetDir);
8875                 fname.append("/");
8876                 }
8877             fname.append(fileSet[i]);
8878             String fullName = parent.resolve(fname);
8880             cmd.append(" ");
8881             cmd.append(fullName);
8882             }
8884         String outString, errString;
8885         if (!executeCommand(cmd.c_str(), "", outString, errString))
8886             {
8887             error("<staticlib> problem: %s", errString.c_str());
8888             return false;
8889             }
8890         removeFromStatCache(getNativePath(fullOut));
8891         return true;
8892         }
8895     virtual bool parse(Element *elem)
8896         {
8897         if (!parent.getAttribute(elem, "command", commandOpt))
8898             return false;
8899         if (!parent.getAttribute(elem, "file", fileNameOpt))
8900             return false;
8901             
8902         std::vector<Element *> children = elem->getChildren();
8903         for (unsigned int i=0 ; i<children.size() ; i++)
8904             {
8905             Element *child = children[i];
8906             String tagName = child->getName();
8907             if (tagName == "fileset")
8908                 {
8909                 if (!parseFileSet(child, parent, fileSet))
8910                     return false;
8911                 }
8912             }
8913         return true;
8914         }
8916 private:
8918     FileSet fileSet;
8920     String commandOpt;
8921     String fileNameOpt;
8923 };
8928 /**
8929  * Strip an executable
8930  */
8931 class TaskStrip : public Task
8933 public:
8935     TaskStrip(MakeBase &par) : Task(par)
8936         { type = TASK_STRIP; name = "strip"; }
8938     virtual ~TaskStrip()
8939         {}
8941     virtual bool execute()
8942         {
8943         String command     = parent.eval(commandOpt, "strip");
8944         String fileName    = parent.eval(fileNameOpt, "");
8945         String symFileName = parent.eval(symFileNameOpt, "");
8947         String fullName = parent.resolve(fileName);
8948         //trace("fullDir:%s", fullDir.c_str());
8949         String cmd;
8950         String outbuf, errbuf;
8952         if (symFileName.size()>0)
8953             {
8954             String symFullName = parent.resolve(symFileName);
8955             cmd = "objcopy --only-keep-debug ";
8956             cmd.append(getNativePath(fullName));
8957             cmd.append(" ");
8958             cmd.append(getNativePath(symFullName));
8959             if (!executeCommand(cmd, "", outbuf, errbuf))
8960                 {
8961                 error("<strip> symbol file failed : %s", errbuf.c_str());
8962                 return false;
8963                 }
8964             }
8965             
8966         cmd = command;
8967         cmd.append(getNativePath(fullName));
8968         if (!executeCommand(cmd, "", outbuf, errbuf))
8969             {
8970             error("<strip> failed : %s", errbuf.c_str());
8971             return false;
8972             }
8973         removeFromStatCache(getNativePath(fullName));
8974         return true;
8975         }
8977     virtual bool parse(Element *elem)
8978         {
8979         if (!parent.getAttribute(elem, "command", commandOpt))
8980             return false;
8981         if (!parent.getAttribute(elem, "file", fileNameOpt))
8982             return false;
8983         if (!parent.getAttribute(elem, "symfile", symFileNameOpt))
8984             return false;
8985         if (fileNameOpt.size() == 0)
8986             {
8987             error("<strip> requires 'file=\"fileName\"' attribute");
8988             return false;
8989             }
8990         return true;
8991         }
8993 private:
8995     String commandOpt;
8996     String fileNameOpt;
8997     String symFileNameOpt;
8998 };
9001 /**
9002  *
9003  */
9004 class TaskTouch : public Task
9006 public:
9008     TaskTouch(MakeBase &par) : Task(par)
9009         { type = TASK_TOUCH; name = "touch"; }
9011     virtual ~TaskTouch()
9012         {}
9014     virtual bool execute()
9015         {
9016         String fileName = parent.eval(fileNameOpt, "");
9018         String fullName = parent.resolve(fileName);
9019         String nativeFile = getNativePath(fullName);
9020         if (!isRegularFile(fullName) && !isDirectory(fullName))
9021             {            
9022             // S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH
9023             int ret = creat(nativeFile.c_str(), 0666);
9024             if (ret != 0) 
9025                 {
9026                 error("<touch> could not create '%s' : %s",
9027                     nativeFile.c_str(), strerror(ret));
9028                 return false;
9029                 }
9030             return true;
9031             }
9032         int ret = utime(nativeFile.c_str(), (struct utimbuf *)0);
9033         if (ret != 0)
9034             {
9035             error("<touch> could not update the modification time for '%s' : %s",
9036                 nativeFile.c_str(), strerror(ret));
9037             return false;
9038             }
9039         removeFromStatCache(nativeFile);
9040         return true;
9041         }
9043     virtual bool parse(Element *elem)
9044         {
9045         //trace("touch parse");
9046         if (!parent.getAttribute(elem, "file", fileNameOpt))
9047             return false;
9048         if (fileNameOpt.size() == 0)
9049             {
9050             error("<touch> requires 'file=\"fileName\"' attribute");
9051             return false;
9052             }
9053         return true;
9054         }
9056     String fileNameOpt;
9057 };
9060 /**
9061  *
9062  */
9063 class TaskTstamp : public Task
9065 public:
9067     TaskTstamp(MakeBase &par) : Task(par)
9068         { type = TASK_TSTAMP; name = "tstamp"; }
9070     virtual ~TaskTstamp()
9071         {}
9073     virtual bool execute()
9074         {
9075         return true;
9076         }
9078     virtual bool parse(Element *elem)
9079         {
9080         //trace("tstamp parse");
9081         return true;
9082         }
9083 };
9087 /**
9088  *
9089  */
9090 Task *Task::createTask(Element *elem, int lineNr)
9092     String tagName = elem->getName();
9093     //trace("task:%s", tagName.c_str());
9094     Task *task = NULL;
9095     if (tagName == "cc")
9096         task = new TaskCC(parent);
9097     else if (tagName == "copy")
9098         task = new TaskCopy(parent);
9099     else if (tagName == "cxxtestpart")
9100         task = new TaskCxxTestPart(parent);
9101     else if (tagName == "cxxtestroot")
9102         task = new TaskCxxTestRoot(parent);
9103     else if (tagName == "cxxtestrun")
9104         task = new TaskCxxTestRun(parent);
9105     else if (tagName == "delete")
9106         task = new TaskDelete(parent);
9107     else if (tagName == "echo")
9108         task = new TaskEcho(parent);
9109     else if (tagName == "jar")
9110         task = new TaskJar(parent);
9111     else if (tagName == "javac")
9112         task = new TaskJavac(parent);
9113     else if (tagName == "link")
9114         task = new TaskLink(parent);
9115     else if (tagName == "makefile")
9116         task = new TaskMakeFile(parent);
9117     else if (tagName == "mkdir")
9118         task = new TaskMkDir(parent);
9119     else if (tagName == "msgfmt")
9120         task = new TaskMsgFmt(parent);
9121     else if (tagName == "pkg-config")
9122         task = new TaskPkgConfig(parent);
9123     else if (tagName == "ranlib")
9124         task = new TaskRanlib(parent);
9125     else if (tagName == "rc")
9126         task = new TaskRC(parent);
9127     else if (tagName == "sharedlib")
9128         task = new TaskSharedLib(parent);
9129     else if (tagName == "staticlib")
9130         task = new TaskStaticLib(parent);
9131     else if (tagName == "strip")
9132         task = new TaskStrip(parent);
9133     else if (tagName == "touch")
9134         task = new TaskTouch(parent);
9135     else if (tagName == "tstamp")
9136         task = new TaskTstamp(parent);
9137     else
9138         {
9139         error("Unknown task '%s'", tagName.c_str());
9140         return NULL;
9141         }
9143     task->setLine(lineNr);
9145     if (!task->parse(elem))
9146         {
9147         delete task;
9148         return NULL;
9149         }
9150     return task;
9155 //########################################################################
9156 //# T A R G E T
9157 //########################################################################
9159 /**
9160  *
9161  */
9162 class Target : public MakeBase
9165 public:
9167     /**
9168      *
9169      */
9170     Target(Make &par) : parent(par)
9171         { init(); }
9173     /**
9174      *
9175      */
9176     Target(const Target &other) : parent(other.parent)
9177         { init(); assign(other); }
9179     /**
9180      *
9181      */
9182     Target &operator=(const Target &other)
9183         { init(); assign(other); return *this; }
9185     /**
9186      *
9187      */
9188     virtual ~Target()
9189         { cleanup() ; }
9192     /**
9193      *
9194      */
9195     virtual Make &getParent()
9196         { return parent; }
9198     /**
9199      *
9200      */
9201     virtual String getName()
9202         { return name; }
9204     /**
9205      *
9206      */
9207     virtual void setName(const String &val)
9208         { name = val; }
9210     /**
9211      *
9212      */
9213     virtual String getDescription()
9214         { return description; }
9216     /**
9217      *
9218      */
9219     virtual void setDescription(const String &val)
9220         { description = val; }
9222     /**
9223      *
9224      */
9225     virtual void addDependency(const String &val)
9226         { deps.push_back(val); }
9228     /**
9229      *
9230      */
9231     virtual void parseDependencies(const String &val)
9232         { deps = tokenize(val, ", "); }
9234     /**
9235      *
9236      */
9237     virtual std::vector<String> &getDependencies()
9238         { return deps; }
9240     /**
9241      *
9242      */
9243     virtual String getIf()
9244         { return ifVar; }
9246     /**
9247      *
9248      */
9249     virtual void setIf(const String &val)
9250         { ifVar = val; }
9252     /**
9253      *
9254      */
9255     virtual String getUnless()
9256         { return unlessVar; }
9258     /**
9259      *
9260      */
9261     virtual void setUnless(const String &val)
9262         { unlessVar = val; }
9264     /**
9265      *
9266      */
9267     virtual void addTask(Task *val)
9268         { tasks.push_back(val); }
9270     /**
9271      *
9272      */
9273     virtual std::vector<Task *> &getTasks()
9274         { return tasks; }
9276 private:
9278     void init()
9279         {
9280         }
9282     void cleanup()
9283         {
9284         tasks.clear();
9285         }
9287     void assign(const Target &other)
9288         {
9289         //parent      = other.parent;
9290         name        = other.name;
9291         description = other.description;
9292         ifVar       = other.ifVar;
9293         unlessVar   = other.unlessVar;
9294         deps        = other.deps;
9295         tasks       = other.tasks;
9296         }
9298     Make &parent;
9300     String name;
9302     String description;
9304     String ifVar;
9306     String unlessVar;
9308     std::vector<String> deps;
9310     std::vector<Task *> tasks;
9312 };
9321 //########################################################################
9322 //# M A K E
9323 //########################################################################
9326 /**
9327  *
9328  */
9329 class Make : public MakeBase
9332 public:
9334     /**
9335      *
9336      */
9337     Make()
9338         { init(); }
9340     /**
9341      *
9342      */
9343     Make(const Make &other)
9344         { assign(other); }
9346     /**
9347      *
9348      */
9349     Make &operator=(const Make &other)
9350         { assign(other); return *this; }
9352     /**
9353      *
9354      */
9355     virtual ~Make()
9356         { cleanup(); }
9358     /**
9359      *
9360      */
9361     virtual std::map<String, Target> &getTargets()
9362         { return targets; }
9365     /**
9366      *
9367      */
9368     virtual String version()
9369         { return BUILDTOOL_VERSION; }
9371     /**
9372      * Overload a <property>
9373      */
9374     virtual bool specifyProperty(const String &name,
9375                                  const String &value);
9377     /**
9378      *
9379      */
9380     virtual bool run();
9382     /**
9383      *
9384      */
9385     virtual bool run(const String &target);
9389 private:
9391     /**
9392      *
9393      */
9394     void init();
9396     /**
9397      *
9398      */
9399     void cleanup();
9401     /**
9402      *
9403      */
9404     void assign(const Make &other);
9406     /**
9407      *
9408      */
9409     bool executeTask(Task &task);
9412     /**
9413      *
9414      */
9415     bool executeTarget(Target &target,
9416              std::set<String> &targetsCompleted);
9419     /**
9420      *
9421      */
9422     bool execute();
9424     /**
9425      *
9426      */
9427     bool checkTargetDependencies(Target &prop,
9428                     std::vector<String> &depList);
9430     /**
9431      *
9432      */
9433     bool parsePropertyFile(const String &fileName,
9434                            const String &prefix);
9436     /**
9437      *
9438      */
9439     bool parseProperty(Element *elem);
9441     /**
9442      *
9443      */
9444     bool parseFile();
9446     /**
9447      *
9448      */
9449     std::vector<String> glob(const String &pattern);
9452     //###############
9453     //# Fields
9454     //###############
9456     String projectName;
9458     String currentTarget;
9460     String defaultTarget;
9462     String specifiedTarget;
9464     String baseDir;
9466     String description;
9467     
9468     //std::vector<Property> properties;
9469     
9470     std::map<String, Target> targets;
9472     std::vector<Task *> allTasks;
9473     
9474     std::map<String, String> specifiedProperties;
9476 };
9479 //########################################################################
9480 //# C L A S S  M A I N T E N A N C E
9481 //########################################################################
9483 /**
9484  *
9485  */
9486 void Make::init()
9488     uri             = "build.xml";
9489     projectName     = "";
9490     currentTarget   = "";
9491     defaultTarget   = "";
9492     specifiedTarget = "";
9493     baseDir         = "";
9494     description     = "";
9495     envPrefix       = "env.";
9496     pcPrefix        = "pc.";
9497     pccPrefix       = "pcc.";
9498     pclPrefix       = "pcl.";
9499     svnPrefix       = "svn.";
9500     properties.clear();
9501     for (unsigned int i = 0 ; i < allTasks.size() ; i++)
9502         delete allTasks[i];
9503     allTasks.clear();
9508 /**
9509  *
9510  */
9511 void Make::cleanup()
9513     for (unsigned int i = 0 ; i < allTasks.size() ; i++)
9514         delete allTasks[i];
9515     allTasks.clear();
9520 /**
9521  *
9522  */
9523 void Make::assign(const Make &other)
9525     uri              = other.uri;
9526     projectName      = other.projectName;
9527     currentTarget    = other.currentTarget;
9528     defaultTarget    = other.defaultTarget;
9529     specifiedTarget  = other.specifiedTarget;
9530     baseDir          = other.baseDir;
9531     description      = other.description;
9532     properties       = other.properties;
9537 //########################################################################
9538 //# U T I L I T Y    T A S K S
9539 //########################################################################
9541 /**
9542  *  Perform a file globbing
9543  */
9544 std::vector<String> Make::glob(const String &pattern)
9546     std::vector<String> res;
9547     return res;
9551 //########################################################################
9552 //# P U B L I C    A P I
9553 //########################################################################
9557 /**
9558  *
9559  */
9560 bool Make::executeTarget(Target &target,
9561              std::set<String> &targetsCompleted)
9564     String name = target.getName();
9566     //First get any dependencies for this target
9567     std::vector<String> deps = target.getDependencies();
9568     for (unsigned int i=0 ; i<deps.size() ; i++)
9569         {
9570         String dep = deps[i];
9571         //Did we do it already?  Skip
9572         if (targetsCompleted.find(dep)!=targetsCompleted.end())
9573             continue;
9574             
9575         std::map<String, Target> &tgts =
9576                target.getParent().getTargets();
9577         std::map<String, Target>::iterator iter =
9578                tgts.find(dep);
9579         if (iter == tgts.end())
9580             {
9581             error("Target '%s' dependency '%s' not found",
9582                       name.c_str(),  dep.c_str());
9583             return false;
9584             }
9585         Target depTarget = iter->second;
9586         if (!executeTarget(depTarget, targetsCompleted))
9587             {
9588             return false;
9589             }
9590         }
9592     status("##### Target : %s\n##### %s", name.c_str(),
9593             target.getDescription().c_str());
9595     //Now let's do the tasks
9596     std::vector<Task *> &tasks = target.getTasks();
9597     for (unsigned int i=0 ; i<tasks.size() ; i++)
9598         {
9599         Task *task = tasks[i];
9600         status("--- %s / %s", name.c_str(), task->getName().c_str());
9601         if (!task->execute())
9602             {
9603             return false;
9604             }
9605         }
9606         
9607     targetsCompleted.insert(name);
9608     
9609     return true;
9614 /**
9615  *  Main execute() method.  Start here and work
9616  *  up the dependency tree 
9617  */
9618 bool Make::execute()
9620     status("######## EXECUTE");
9622     //Determine initial target
9623     if (specifiedTarget.size()>0)
9624         {
9625         currentTarget = specifiedTarget;
9626         }
9627     else if (defaultTarget.size()>0)
9628         {
9629         currentTarget = defaultTarget;
9630         }
9631     else
9632         {
9633         error("execute: no specified or default target requested");
9634         return false;
9635         }
9637     std::map<String, Target>::iterator iter =
9638                targets.find(currentTarget);
9639     if (iter == targets.end())
9640         {
9641         error("Initial target '%s' not found",
9642                  currentTarget.c_str());
9643         return false;
9644         }
9645         
9646     //Now run
9647     Target target = iter->second;
9648     std::set<String> targetsCompleted;
9649     if (!executeTarget(target, targetsCompleted))
9650         {
9651         return false;
9652         }
9654     status("######## EXECUTE COMPLETE");
9655     return true;
9661 /**
9662  *
9663  */
9664 bool Make::checkTargetDependencies(Target &target, 
9665                             std::vector<String> &depList)
9667     String tgtName = target.getName().c_str();
9668     depList.push_back(tgtName);
9670     std::vector<String> deps = target.getDependencies();
9671     for (unsigned int i=0 ; i<deps.size() ; i++)
9672         {
9673         String dep = deps[i];
9674         //First thing entered was the starting Target
9675         if (dep == depList[0])
9676             {
9677             error("Circular dependency '%s' found at '%s'",
9678                       dep.c_str(), tgtName.c_str());
9679             std::vector<String>::iterator diter;
9680             for (diter=depList.begin() ; diter!=depList.end() ; diter++)
9681                 {
9682                 error("  %s", diter->c_str());
9683                 }
9684             return false;
9685             }
9687         std::map<String, Target> &tgts =
9688                   target.getParent().getTargets();
9689         std::map<String, Target>::iterator titer = tgts.find(dep);
9690         if (titer == tgts.end())
9691             {
9692             error("Target '%s' dependency '%s' not found",
9693                       tgtName.c_str(), dep.c_str());
9694             return false;
9695             }
9696         if (!checkTargetDependencies(titer->second, depList))
9697             {
9698             return false;
9699             }
9700         }
9701     return true;
9708 static int getword(int pos, const String &inbuf, String &result)
9710     int p = pos;
9711     int len = (int)inbuf.size();
9712     String val;
9713     while (p < len)
9714         {
9715         char ch = inbuf[p];
9716         if (!isalnum(ch) && ch!='.' && ch!='_')
9717             break;
9718         val.push_back(ch);
9719         p++;
9720         }
9721     result = val;
9722     return p;
9728 /**
9729  *
9730  */
9731 bool Make::parsePropertyFile(const String &fileName,
9732                              const String &prefix)
9734     FILE *f = fopen(fileName.c_str(), "r");
9735     if (!f)
9736         {
9737         error("could not open property file %s", fileName.c_str());
9738         return false;
9739         }
9740     int linenr = 0;
9741     while (!feof(f))
9742         {
9743         char buf[256];
9744         if (!fgets(buf, 255, f))
9745             break;
9746         linenr++;
9747         String s = buf;
9748         s = trim(s);
9749         int len = s.size();
9750         if (len == 0)
9751             continue;
9752         if (s[0] == '#')
9753             continue;
9754         String key;
9755         String val;
9756         int p = 0;
9757         int p2 = getword(p, s, key);
9758         if (p2 <= p)
9759             {
9760             error("property file %s, line %d: expected keyword",
9761                     fileName.c_str(), linenr);
9762             return false;
9763             }
9764         if (prefix.size() > 0)
9765             {
9766             key.insert(0, prefix);
9767             }
9769         //skip whitespace
9770         for (p=p2 ; p<len ; p++)
9771             if (!isspace(s[p]))
9772                 break;
9774         if (p>=len || s[p]!='=')
9775             {
9776             error("property file %s, line %d: expected '='",
9777                     fileName.c_str(), linenr);
9778             return false;
9779             }
9780         p++;
9782         //skip whitespace
9783         for ( ; p<len ; p++)
9784             if (!isspace(s[p]))
9785                 break;
9787         /* This way expects a word after the =
9788         p2 = getword(p, s, val);
9789         if (p2 <= p)
9790             {
9791             error("property file %s, line %d: expected value",
9792                     fileName.c_str(), linenr);
9793             return false;
9794             }
9795         */
9796         // This way gets the rest of the line after the =
9797         if (p>=len)
9798             {
9799             error("property file %s, line %d: expected value",
9800                     fileName.c_str(), linenr);
9801             return false;
9802             }
9803         val = s.substr(p);
9804         if (key.size()==0)
9805             continue;
9806         //allow property to be set, even if val=""
9808         //trace("key:'%s' val:'%s'", key.c_str(), val.c_str());
9809         //See if we wanted to overload this property
9810         std::map<String, String>::iterator iter =
9811             specifiedProperties.find(key);
9812         if (iter!=specifiedProperties.end())
9813             {
9814             val = iter->second;
9815             status("overloading property '%s' = '%s'",
9816                    key.c_str(), val.c_str());
9817             }
9818         properties[key] = val;
9819         }
9820     fclose(f);
9821     return true;
9827 /**
9828  *
9829  */
9830 bool Make::parseProperty(Element *elem)
9832     std::vector<Attribute> &attrs = elem->getAttributes();
9833     for (unsigned int i=0 ; i<attrs.size() ; i++)
9834         {
9835         String attrName = attrs[i].getName();
9836         String attrVal  = attrs[i].getValue();
9838         if (attrName == "name")
9839             {
9840             String val;
9841             if (!getAttribute(elem, "value", val))
9842                 return false;
9843             if (val.size() > 0)
9844                 {
9845                 properties[attrVal] = val;
9846                 }
9847             else
9848                 {
9849                 if (!getAttribute(elem, "location", val))
9850                     return false;
9851                 //let the property exist, even if not defined
9852                 properties[attrVal] = val;
9853                 }
9854             //See if we wanted to overload this property
9855             std::map<String, String>::iterator iter =
9856                 specifiedProperties.find(attrVal);
9857             if (iter != specifiedProperties.end())
9858                 {
9859                 val = iter->second;
9860                 status("overloading property '%s' = '%s'",
9861                     attrVal.c_str(), val.c_str());
9862                 properties[attrVal] = val;
9863                 }
9864             }
9865         else if (attrName == "file")
9866             {
9867             String prefix;
9868             if (!getAttribute(elem, "prefix", prefix))
9869                 return false;
9870             if (prefix.size() > 0)
9871                 {
9872                 if (prefix[prefix.size()-1] != '.')
9873                     prefix.push_back('.');
9874                 }
9875             if (!parsePropertyFile(attrName, prefix))
9876                 return false;
9877             }
9878         else if (attrName == "environment")
9879             {
9880             if (attrVal.find('.') != attrVal.npos)
9881                 {
9882                 error("environment prefix cannot have a '.' in it");
9883                 return false;
9884                 }
9885             envPrefix = attrVal;
9886             envPrefix.push_back('.');
9887             }
9888         else if (attrName == "pkg-config")
9889             {
9890             if (attrVal.find('.') != attrVal.npos)
9891                 {
9892                 error("pkg-config prefix cannot have a '.' in it");
9893                 return false;
9894                 }
9895             pcPrefix = attrVal;
9896             pcPrefix.push_back('.');
9897             }
9898         else if (attrName == "pkg-config-cflags")
9899             {
9900             if (attrVal.find('.') != attrVal.npos)
9901                 {
9902                 error("pkg-config-cflags prefix cannot have a '.' in it");
9903                 return false;
9904                 }
9905             pccPrefix = attrVal;
9906             pccPrefix.push_back('.');
9907             }
9908         else if (attrName == "pkg-config-libs")
9909             {
9910             if (attrVal.find('.') != attrVal.npos)
9911                 {
9912                 error("pkg-config-libs prefix cannot have a '.' in it");
9913                 return false;
9914                 }
9915             pclPrefix = attrVal;
9916             pclPrefix.push_back('.');
9917             }
9918         else if (attrName == "subversion")
9919             {
9920             if (attrVal.find('.') != attrVal.npos)
9921                 {
9922                 error("subversion prefix cannot have a '.' in it");
9923                 return false;
9924                 }
9925             svnPrefix = attrVal;
9926             svnPrefix.push_back('.');
9927             }
9928         }
9930     return true;
9936 /**
9937  *
9938  */
9939 bool Make::parseFile()
9941     status("######## PARSE : %s", uri.getPath().c_str());
9943     setLine(0);
9945     Parser parser;
9946     Element *root = parser.parseFile(uri.getNativePath());
9947     if (!root)
9948         {
9949         error("Could not open %s for reading",
9950               uri.getNativePath().c_str());
9951         return false;
9952         }
9953     
9954     setLine(root->getLine());
9956     if (root->getChildren().size()==0 ||
9957         root->getChildren()[0]->getName()!="project")
9958         {
9959         error("Main xml element should be <project>");
9960         delete root;
9961         return false;
9962         }
9964     //########## Project attributes
9965     Element *project = root->getChildren()[0];
9966     String s = project->getAttribute("name");
9967     if (s.size() > 0)
9968         projectName = s;
9969     s = project->getAttribute("default");
9970     if (s.size() > 0)
9971         defaultTarget = s;
9972     s = project->getAttribute("basedir");
9973     if (s.size() > 0)
9974         baseDir = s;
9976     //######### PARSE MEMBERS
9977     std::vector<Element *> children = project->getChildren();
9978     for (unsigned int i=0 ; i<children.size() ; i++)
9979         {
9980         Element *elem = children[i];
9981         setLine(elem->getLine());
9982         String tagName = elem->getName();
9984         //########## DESCRIPTION
9985         if (tagName == "description")
9986             {
9987             description = parser.trim(elem->getValue());
9988             }
9990         //######### PROPERTY
9991         else if (tagName == "property")
9992             {
9993             if (!parseProperty(elem))
9994                 return false;
9995             }
9997         //######### TARGET
9998         else if (tagName == "target")
9999             {
10000             String tname   = elem->getAttribute("name");
10001             String tdesc   = elem->getAttribute("description");
10002             String tdeps   = elem->getAttribute("depends");
10003             String tif     = elem->getAttribute("if");
10004             String tunless = elem->getAttribute("unless");
10005             Target target(*this);
10006             target.setName(tname);
10007             target.setDescription(tdesc);
10008             target.parseDependencies(tdeps);
10009             target.setIf(tif);
10010             target.setUnless(tunless);
10011             std::vector<Element *> telems = elem->getChildren();
10012             for (unsigned int i=0 ; i<telems.size() ; i++)
10013                 {
10014                 Element *telem = telems[i];
10015                 Task breeder(*this);
10016                 Task *task = breeder.createTask(telem, telem->getLine());
10017                 if (!task)
10018                     return false;
10019                 allTasks.push_back(task);
10020                 target.addTask(task);
10021                 }
10023             //Check name
10024             if (tname.size() == 0)
10025                 {
10026                 error("no name for target");
10027                 return false;
10028                 }
10029             //Check for duplicate name
10030             if (targets.find(tname) != targets.end())
10031                 {
10032                 error("target '%s' already defined", tname.c_str());
10033                 return false;
10034                 }
10035             //more work than targets[tname]=target, but avoids default allocator
10036             targets.insert(std::make_pair<String, Target>(tname, target));
10037             }
10038         //######### none of the above
10039         else
10040             {
10041             error("unknown toplevel tag: <%s>", tagName.c_str());
10042             return false;
10043             }
10045         }
10047     std::map<String, Target>::iterator iter;
10048     for (iter = targets.begin() ; iter!= targets.end() ; iter++)
10049         {
10050         Target tgt = iter->second;
10051         std::vector<String> depList;
10052         if (!checkTargetDependencies(tgt, depList))
10053             {
10054             return false;
10055             }
10056         }
10059     delete root;
10060     status("######## PARSE COMPLETE");
10061     return true;
10065 /**
10066  * Overload a <property>
10067  */
10068 bool Make::specifyProperty(const String &name, const String &value)
10070     if (specifiedProperties.find(name) != specifiedProperties.end())
10071         {
10072         error("Property %s already specified", name.c_str());
10073         return false;
10074         }
10075     specifiedProperties[name] = value;
10076     return true;
10081 /**
10082  *
10083  */
10084 bool Make::run()
10086     if (!parseFile())
10087         return false;
10088         
10089     if (!execute())
10090         return false;
10092     return true;
10098 /**
10099  * Get a formatted MM:SS.sss time elapsed string
10100  */ 
10101 static String
10102 timeDiffString(struct timeval &x, struct timeval &y)
10104     long microsX  = x.tv_usec;
10105     long secondsX = x.tv_sec;
10106     long microsY  = y.tv_usec;
10107     long secondsY = y.tv_sec;
10108     if (microsX < microsY)
10109         {
10110         microsX += 1000000;
10111         secondsX -= 1;
10112         }
10114     int seconds = (int)(secondsX - secondsY);
10115     int millis  = (int)((microsX - microsY)/1000);
10117     int minutes = seconds/60;
10118     seconds -= minutes*60;
10119     char buf[80];
10120     snprintf(buf, 79, "%dm %d.%03ds", minutes, seconds, millis);
10121     String ret = buf;
10122     return ret;
10123     
10126 /**
10127  *
10128  */
10129 bool Make::run(const String &target)
10131     status("####################################################");
10132     status("#   %s", version().c_str());
10133     status("####################################################");
10134     struct timeval timeStart, timeEnd;
10135     ::gettimeofday(&timeStart, NULL);
10136     specifiedTarget = target;
10137     if (!run())
10138         return false;
10139     ::gettimeofday(&timeEnd, NULL);
10140     String timeStr = timeDiffString(timeEnd, timeStart);
10141     status("####################################################");
10142     status("#   BuildTool Completed : %s", timeStr.c_str());
10143     status("####################################################");
10144     return true;
10153 }// namespace buildtool
10154 //########################################################################
10155 //# M A I N
10156 //########################################################################
10158 typedef buildtool::String String;
10160 /**
10161  *  Format an error message in printf() style
10162  */
10163 static void error(const char *fmt, ...)
10165     va_list ap;
10166     va_start(ap, fmt);
10167     fprintf(stderr, "BuildTool error: ");
10168     vfprintf(stderr, fmt, ap);
10169     fprintf(stderr, "\n");
10170     va_end(ap);
10174 static bool parseProperty(const String &s, String &name, String &val)
10176     int len = s.size();
10177     int i;
10178     for (i=0 ; i<len ; i++)
10179         {
10180         char ch = s[i];
10181         if (ch == '=')
10182             break;
10183         name.push_back(ch);
10184         }
10185     if (i>=len || s[i]!='=')
10186         {
10187         error("property requires -Dname=value");
10188         return false;
10189         }
10190     i++;
10191     for ( ; i<len ; i++)
10192         {
10193         char ch = s[i];
10194         val.push_back(ch);
10195         }
10196     return true;
10200 /**
10201  * Compare a buffer with a key, for the length of the key
10202  */
10203 static bool sequ(const String &buf, const char *key)
10205     int len = buf.size();
10206     for (int i=0 ; key[i] && i<len ; i++)
10207         {
10208         if (key[i] != buf[i])
10209             return false;
10210         }        
10211     return true;
10214 static void usage(int argc, char **argv)
10216     printf("usage:\n");
10217     printf("   %s [options] [target]\n", argv[0]);
10218     printf("Options:\n");
10219     printf("  -help, -h              print this message\n");
10220     printf("  -version               print the version information and exit\n");
10221     printf("  -file <file>           use given buildfile\n");
10222     printf("  -f <file>                 ''\n");
10223     printf("  -D<property>=<value>   use value for given property\n");
10229 /**
10230  * Parse the command-line args, get our options,
10231  * and run this thing
10232  */   
10233 static bool parseOptions(int argc, char **argv)
10235     if (argc < 1)
10236         {
10237         error("Cannot parse arguments");
10238         return false;
10239         }
10241     buildtool::Make make;
10243     String target;
10245     //char *progName = argv[0];
10246     for (int i=1 ; i<argc ; i++)
10247         {
10248         String arg = argv[i];
10249         if (arg.size()>1 && arg[0]=='-')
10250             {
10251             if (arg == "-h" || arg == "-help")
10252                 {
10253                 usage(argc,argv);
10254                 return true;
10255                 }
10256             else if (arg == "-version")
10257                 {
10258                 printf("%s", make.version().c_str());
10259                 return true;
10260                 }
10261             else if (arg == "-f" || arg == "-file")
10262                 {
10263                 if (i>=argc)
10264                    {
10265                    usage(argc, argv);
10266                    return false;
10267                    }
10268                 i++; //eat option
10269                 make.setURI(argv[i]);
10270                 }
10271             else if (arg.size()>2 && sequ(arg, "-D"))
10272                 {
10273                 String s = arg.substr(2, arg.size());
10274                 String name, value;
10275                 if (!parseProperty(s, name, value))
10276                    {
10277                    usage(argc, argv);
10278                    return false;
10279                    }
10280                 if (!make.specifyProperty(name, value))
10281                     return false;
10282                 }
10283             else
10284                 {
10285                 error("Unknown option:%s", arg.c_str());
10286                 return false;
10287                 }
10288             }
10289         else
10290             {
10291             if (target.size()>0)
10292                 {
10293                 error("only one initial target");
10294                 usage(argc, argv);
10295                 return false;
10296                 }
10297             target = arg;
10298             }
10299         }
10301     //We have the options.  Now execute them
10302     if (!make.run(target))
10303         return false;
10305     return true;
10312 static bool runMake()
10314     buildtool::Make make;
10315     if (!make.run())
10316         return false;
10317     return true;
10321 static bool pkgConfigTest()
10323     buildtool::PkgConfig pkgConfig;
10324     if (!pkgConfig.readFile("gtk+-2.0.pc"))
10325         return false;
10326     return true;
10331 static bool depTest()
10333     buildtool::DepTool deptool;
10334     deptool.setSourceDirectory("/dev/ink/inkscape/src");
10335     if (!deptool.generateDependencies("build.dep"))
10336         return false;
10337     std::vector<buildtool::FileRec> res =
10338            deptool.loadDepFile("build.dep");
10339     if (res.size() == 0)
10340         return false;
10341     return true;
10344 static bool popenTest()
10346     buildtool::Make make;
10347     buildtool::String out, err;
10348     bool ret = make.executeCommand("gcc xx.cpp", "", out, err);
10349     printf("Popen test:%d '%s' '%s'\n", ret, out.c_str(), err.c_str());
10350     return true;
10354 static bool propFileTest()
10356     buildtool::Make make;
10357     make.parsePropertyFile("test.prop", "test.");
10358     return true;
10362 int main(int argc, char **argv)
10365     if (!parseOptions(argc, argv))
10366         return 1;
10367     /*
10368     if (!popenTest())
10369         return 1;
10371     if (!depTest())
10372         return 1;
10373     if (!propFileTest())
10374         return 1;
10375     if (runMake())
10376         return 1;
10377     */
10378     return 0;
10382 //########################################################################
10383 //# E N D 
10384 //########################################################################