Code

23d265f02a6ebbfa5a81ecb39e1939008736d508
[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.6"
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="pcl"/>
3193      *             ${pcl.gtkmm}
3194      */
3195     String pclPrefix;
3201     /**
3202      *  Print a printf()-like formatted error message
3203      */
3204     void error(const char *fmt, ...);
3206     /**
3207      *  Print a printf()-like formatted trace message
3208      */
3209     void status(const char *fmt, ...);
3211     /**
3212      *  Show target status
3213      */
3214     void targetstatus(const char *fmt, ...);
3216     /**
3217      *  Print a printf()-like formatted trace message
3218      */
3219     void trace(const char *fmt, ...);
3221     /**
3222      *  Check if a given string matches a given regex pattern
3223      */
3224     bool regexMatch(const String &str, const String &pattern);
3226     /**
3227      *
3228      */
3229     String getSuffix(const String &fname);
3231     /**
3232      * Break up a string into substrings delimited the characters
3233      * in delimiters.  Null-length substrings are ignored
3234      */  
3235     std::vector<String> tokenize(const String &val,
3236                           const String &delimiters);
3238     /**
3239      *  replace runs of whitespace with a space
3240      */
3241     String strip(const String &s);
3243     /**
3244      *  remove leading whitespace from each line
3245      */
3246     String leftJustify(const String &s);
3248     /**
3249      *  remove leading and trailing whitespace from string
3250      */
3251     String trim(const String &s);
3253     /**
3254      *  Return a lower case version of the given string
3255      */
3256     String toLower(const String &s);
3258     /**
3259      * Return the native format of the canonical
3260      * path which we store
3261      */
3262     String getNativePath(const String &path);
3264     /**
3265      * Execute a shell command.  Outbuf is a ref to a string
3266      * to catch the result.     
3267      */         
3268     bool executeCommand(const String &call,
3269                         const String &inbuf,
3270                         String &outbuf,
3271                         String &errbuf);
3272     /**
3273      * List all directories in a given base and starting directory
3274      * It is usually called like:
3275      *        bool ret = listDirectories("src", "", result);    
3276      */         
3277     bool listDirectories(const String &baseName,
3278                          const String &dirname,
3279                          std::vector<String> &res);
3281     /**
3282      * Find all files in the named directory 
3283      */         
3284     bool listFiles(const String &baseName,
3285                    const String &dirname,
3286                    std::vector<String> &result);
3288     /**
3289      * Perform a listing for a fileset 
3290      */         
3291     bool listFiles(MakeBase &propRef, FileSet &fileSet);
3293     /**
3294      * Parse a <patternset>
3295      */  
3296     bool parsePatternSet(Element *elem,
3297                        MakeBase &propRef,
3298                        std::vector<String> &includes,
3299                        std::vector<String> &excludes);
3301     /**
3302      * Parse a <fileset> entry, and determine which files
3303      * should be included
3304      */  
3305     bool parseFileSet(Element *elem,
3306                     MakeBase &propRef,
3307                     FileSet &fileSet);
3308     /**
3309      * Parse a <filelist> entry
3310      */  
3311     bool parseFileList(Element *elem,
3312                     MakeBase &propRef,
3313                     FileList &fileList);
3315     /**
3316      * Return this object's property list
3317      */
3318     virtual std::map<String, String> &getProperties()
3319         { return properties; }
3322     std::map<String, String> properties;
3324     /**
3325      * Create a directory, making intermediate dirs
3326      * if necessary
3327      */                  
3328     bool createDirectory(const String &dirname);
3330     /**
3331      * Delete a directory and its children if desired
3332      */
3333     bool removeDirectory(const String &dirName);
3335     /**
3336      * Copy a file from one name to another. Perform only if needed
3337      */ 
3338     bool copyFile(const String &srcFile, const String &destFile);
3340     /**
3341      * Delete a file
3342      */ 
3343     bool removeFile(const String &file);
3345     /**
3346      * Tests if the file exists
3347      */ 
3348     bool fileExists(const String &fileName);
3350     /**
3351      * Tests if the file exists and is a regular file
3352      */ 
3353     bool isRegularFile(const String &fileName);
3355     /**
3356      * Tests if the file exists and is a directory
3357      */ 
3358     bool isDirectory(const String &fileName);
3360     /**
3361      * Tests is the modification date of fileA is newer than fileB
3362      */ 
3363     bool isNewerThan(const String &fileA, const String &fileB);
3365 private:
3367     bool pkgConfigRecursive(const String packageName,
3368                             const String &path, 
3369                             const String &prefix, 
3370                             int query,
3371                             String &result,
3372                             std::set<String> &deplist);
3374     /**
3375      * utility method to query for "all", "cflags", or "libs" for this package and its
3376      * dependencies.  0, 1, 2
3377      */          
3378     bool pkgConfigQuery(const String &packageName, int query, String &result);
3380     /**
3381      * replace a variable ref like ${a} with a value
3382      */
3383     bool lookupProperty(const String &s, String &result);
3384     
3385     /**
3386      * called by getSubstitutions().  This is in case a looked-up string
3387      * has substitutions also.     
3388      */
3389     bool getSubstitutionsRecursive(const String &s, String &result, int depth);
3391     /**
3392      * replace variable refs in a string like ${a} with their values
3393      */
3394     bool getSubstitutions(const String &s, String &result);
3396     int line;
3399 };
3403 /**
3404  * Define the pkg-config class here, since it will be used in MakeBase method
3405  * implementations. 
3406  */
3407 class PkgConfig : public MakeBase
3410 public:
3412     /**
3413      *
3414      */
3415     PkgConfig()
3416         {
3417          path   = ".";
3418          prefix = "/target";
3419          init();
3420          }
3422     /**
3423      *
3424      */
3425     PkgConfig(const PkgConfig &other)
3426         { assign(other); }
3428     /**
3429      *
3430      */
3431     PkgConfig &operator=(const PkgConfig &other)
3432         { assign(other); return *this; }
3434     /**
3435      *
3436      */
3437     virtual ~PkgConfig()
3438         { }
3440     /**
3441      *
3442      */
3443     virtual String getName()
3444         { return name; }
3446     /**
3447      *
3448      */
3449     virtual String getPath()
3450         { return path; }
3452     /**
3453      *
3454      */
3455     virtual void setPath(const String &val)
3456         { path = val; }
3458     /**
3459      *
3460      */
3461     virtual String getPrefix()
3462         { return prefix; }
3464     /**
3465      *  Allow the user to override the prefix in the file
3466      */
3467     virtual void setPrefix(const String &val)
3468         { prefix = val; }
3470     /**
3471      *
3472      */
3473     virtual String getDescription()
3474         { return description; }
3476     /**
3477      *
3478      */
3479     virtual String getCflags()
3480         { return cflags; }
3482     /**
3483      *
3484      */
3485     virtual String getLibs()
3486         { return libs; }
3488     /**
3489      *
3490      */
3491     virtual String getAll()
3492         {
3493          String ret = cflags;
3494          ret.append(" ");
3495          ret.append(libs);
3496          return ret;
3497         }
3499     /**
3500      *
3501      */
3502     virtual String getVersion()
3503         { return version; }
3505     /**
3506      *
3507      */
3508     virtual int getMajorVersion()
3509         { return majorVersion; }
3511     /**
3512      *
3513      */
3514     virtual int getMinorVersion()
3515         { return minorVersion; }
3517     /**
3518      *
3519      */
3520     virtual int getMicroVersion()
3521         { return microVersion; }
3523     /**
3524      *
3525      */
3526     virtual std::map<String, String> &getAttributes()
3527         { return attrs; }
3529     /**
3530      *
3531      */
3532     virtual std::vector<String> &getRequireList()
3533         { return requireList; }
3535     /**
3536      *  Read a file for its details
3537      */         
3538     virtual bool readFile(const String &fileName);
3540     /**
3541      *  Read a file for its details
3542      */         
3543     virtual bool query(const String &name);
3545 private:
3547     void init()
3548         {
3549         //do not set path and prefix here
3550         name         = "";
3551         description  = "";
3552         cflags       = "";
3553         libs         = "";
3554         requires     = "";
3555         version      = "";
3556         majorVersion = 0;
3557         minorVersion = 0;
3558         microVersion = 0;
3559         fileName     = "";
3560         attrs.clear();
3561         requireList.clear();
3562         }
3564     void assign(const PkgConfig &other)
3565         {
3566         name         = other.name;
3567         path         = other.path;
3568         prefix       = other.prefix;
3569         description  = other.description;
3570         cflags       = other.cflags;
3571         libs         = other.libs;
3572         requires     = other.requires;
3573         version      = other.version;
3574         majorVersion = other.majorVersion;
3575         minorVersion = other.minorVersion;
3576         microVersion = other.microVersion;
3577         fileName     = other.fileName;
3578         attrs        = other.attrs;
3579         requireList  = other.requireList;
3580         }
3584     int get(int pos);
3586     int skipwhite(int pos);
3588     int getword(int pos, String &ret);
3590     /**
3591      * Very important
3592      */         
3593     bool parseRequires();
3595     void parseVersion();
3597     bool parseLine(const String &lineBuf);
3599     bool parse(const String &buf);
3601     void dumpAttrs();
3603     String name;
3605     String path;
3607     String prefix;
3609     String description;
3611     String cflags;
3613     String libs;
3615     String requires;
3617     String version;
3619     int majorVersion;
3621     int minorVersion;
3623     int microVersion;
3625     String fileName;
3627     std::map<String, String> attrs;
3629     std::vector<String> requireList;
3631     char *parsebuf;
3632     int parselen;
3633 };
3638 /**
3639  *  Print a printf()-like formatted error message
3640  */
3641 void MakeBase::error(const char *fmt, ...)
3643     va_list args;
3644     va_start(args,fmt);
3645     fprintf(stderr, "Make error line %d: ", line);
3646     vfprintf(stderr, fmt, args);
3647     fprintf(stderr, "\n");
3648     va_end(args) ;
3653 /**
3654  *  Print a printf()-like formatted trace message
3655  */
3656 void MakeBase::status(const char *fmt, ...)
3658     va_list args;
3659     //fprintf(stdout, " ");
3660     va_start(args,fmt);
3661     vfprintf(stdout, fmt, args);
3662     va_end(args);
3663     fprintf(stdout, "\n");
3664     fflush(stdout);
3668 /**
3669  *  Print a printf()-like formatted trace message
3670  */
3671 void MakeBase::trace(const char *fmt, ...)
3673     va_list args;
3674     fprintf(stdout, "Make: ");
3675     va_start(args,fmt);
3676     vfprintf(stdout, fmt, args);
3677     va_end(args) ;
3678     fprintf(stdout, "\n");
3679     fflush(stdout);
3684 /**
3685  *  Resolve another path relative to this one
3686  */
3687 String MakeBase::resolve(const String &otherPath)
3689     URI otherURI(otherPath);
3690     URI fullURI = uri.resolve(otherURI);
3691     String ret = fullURI.toString();
3692     return ret;
3697 /**
3698  *  Check if a given string matches a given regex pattern
3699  */
3700 bool MakeBase::regexMatch(const String &str, const String &pattern)
3702     const TRexChar *terror = NULL;
3703     const TRexChar *cpat = pattern.c_str();
3704     TRex *expr = trex_compile(cpat, &terror);
3705     if (!expr)
3706         {
3707         if (!terror)
3708             terror = "undefined";
3709         error("compilation error [%s]!\n", terror);
3710         return false;
3711         } 
3713     bool ret = true;
3715     const TRexChar *cstr = str.c_str();
3716     if (trex_match(expr, cstr))
3717         {
3718         ret = true;
3719         }
3720     else
3721         {
3722         ret = false;
3723         }
3725     trex_free(expr);
3727     return ret;
3730 /**
3731  *  Return the suffix, if any, of a file name
3732  */
3733 String MakeBase::getSuffix(const String &fname)
3735     if (fname.size() < 2)
3736         return "";
3737     unsigned int pos = fname.find_last_of('.');
3738     if (pos == fname.npos)
3739         return "";
3740     pos++;
3741     String res = fname.substr(pos, fname.size()-pos);
3742     //trace("suffix:%s", res.c_str()); 
3743     return res;
3748 /**
3749  * Break up a string into substrings delimited the characters
3750  * in delimiters.  Null-length substrings are ignored
3751  */  
3752 std::vector<String> MakeBase::tokenize(const String &str,
3753                                 const String &delimiters)
3756     std::vector<String> res;
3757     char *del = (char *)delimiters.c_str();
3758     String dmp;
3759     for (unsigned int i=0 ; i<str.size() ; i++)
3760         {
3761         char ch = str[i];
3762         char *p = (char *)0;
3763         for (p=del ; *p ; p++)
3764             if (*p == ch)
3765                 break;
3766         if (*p)
3767             {
3768             if (dmp.size() > 0)
3769                 {
3770                 res.push_back(dmp);
3771                 dmp.clear();
3772                 }
3773             }
3774         else
3775             {
3776             dmp.push_back(ch);
3777             }
3778         }
3779     //Add tail
3780     if (dmp.size() > 0)
3781         {
3782         res.push_back(dmp);
3783         dmp.clear();
3784         }
3786     return res;
3791 /**
3792  *  replace runs of whitespace with a single space
3793  */
3794 String MakeBase::strip(const String &s)
3796     int len = s.size();
3797     String stripped;
3798     for (int i = 0 ; i<len ; i++)
3799         {
3800         char ch = s[i];
3801         if (isspace(ch))
3802             {
3803             stripped.push_back(' ');
3804             for ( ; i<len ; i++)
3805                 {
3806                 ch = s[i];
3807                 if (!isspace(ch))
3808                     {
3809                     stripped.push_back(ch);
3810                     break;
3811                     }
3812                 }
3813             }
3814         else
3815             {
3816             stripped.push_back(ch);
3817             }
3818         }
3819     return stripped;
3822 /**
3823  *  remove leading whitespace from each line
3824  */
3825 String MakeBase::leftJustify(const String &s)
3827     String out;
3828     int len = s.size();
3829     for (int i = 0 ; i<len ; )
3830         {
3831         char ch;
3832         //Skip to first visible character
3833         while (i<len)
3834             {
3835             ch = s[i];
3836             if (ch == '\n' || ch == '\r'
3837               || !isspace(ch))
3838                   break;
3839             i++;
3840             }
3841         //Copy the rest of the line
3842         while (i<len)
3843             {
3844             ch = s[i];
3845             if (ch == '\n' || ch == '\r')
3846                 {
3847                 if (ch != '\r')
3848                     out.push_back('\n');
3849                 i++;
3850                 break;
3851                 }
3852             else
3853                 {
3854                 out.push_back(ch);
3855                 }
3856             i++;
3857             }
3858         }
3859     return out;
3863 /**
3864  *  Removes whitespace from beginning and end of a string
3865  */
3866 String MakeBase::trim(const String &s)
3868     if (s.size() < 1)
3869         return s;
3870     
3871     //Find first non-ws char
3872     unsigned int begin = 0;
3873     for ( ; begin < s.size() ; begin++)
3874         {
3875         if (!isspace(s[begin]))
3876             break;
3877         }
3879     //Find first non-ws char, going in reverse
3880     unsigned int end = s.size() - 1;
3881     for ( ; end > begin ; end--)
3882         {
3883         if (!isspace(s[end]))
3884             break;
3885         }
3886     //trace("begin:%d  end:%d", begin, end);
3888     String res = s.substr(begin, end-begin+1);
3889     return res;
3893 /**
3894  *  Return a lower case version of the given string
3895  */
3896 String MakeBase::toLower(const String &s)
3898     if (s.size()==0)
3899         return s;
3901     String ret;
3902     for(unsigned int i=0; i<s.size() ; i++)
3903         {
3904         ret.push_back(tolower(s[i]));
3905         }
3906     return ret;
3910 /**
3911  * Return the native format of the canonical
3912  * path which we store
3913  */
3914 String MakeBase::getNativePath(const String &path)
3916 #ifdef __WIN32__
3917     String npath;
3918     unsigned int firstChar = 0;
3919     if (path.size() >= 3)
3920         {
3921         if (path[0] == '/' &&
3922             isalpha(path[1]) &&
3923             path[2] == ':')
3924             firstChar++;
3925         }
3926     for (unsigned int i=firstChar ; i<path.size() ; i++)
3927         {
3928         char ch = path[i];
3929         if (ch == '/')
3930             npath.push_back('\\');
3931         else
3932             npath.push_back(ch);
3933         }
3934     return npath;
3935 #else
3936     return path;
3937 #endif
3941 #ifdef __WIN32__
3942 #include <tchar.h>
3944 static String win32LastError()
3947     DWORD dw = GetLastError(); 
3949     LPVOID str;
3950     FormatMessage(
3951         FORMAT_MESSAGE_ALLOCATE_BUFFER | 
3952         FORMAT_MESSAGE_FROM_SYSTEM,
3953         NULL,
3954         dw,
3955         0,
3956         (LPTSTR) &str,
3957         0, NULL );
3958     LPTSTR p = _tcschr((const char *)str, _T('\r'));
3959     if(p != NULL)
3960         { // lose CRLF
3961         *p = _T('\0');
3962         }
3963     String ret = (char *)str;
3964     LocalFree(str);
3966     return ret;
3968 #endif
3973 #ifdef __WIN32__
3975 /**
3976  * Execute a system call, using pipes to send data to the
3977  * program's stdin,  and reading stdout and stderr.
3978  */
3979 bool MakeBase::executeCommand(const String &command,
3980                               const String &inbuf,
3981                               String &outbuf,
3982                               String &errbuf)
3985     status("============ cmd ============\n%s\n=============================",
3986                 command.c_str());
3988     outbuf.clear();
3989     errbuf.clear();
3990     
3992     /*
3993     I really hate having win32 code in this program, but the
3994     read buffer in command.com and cmd.exe are just too small
3995     for the large commands we need for compiling and linking.
3996     */
3998     bool ret = true;
4000     //# Allocate a separate buffer for safety
4001     char *paramBuf = new char[command.size() + 1];
4002     if (!paramBuf)
4003        {
4004        error("executeCommand cannot allocate command buffer");
4005        return false;
4006        }
4007     strcpy(paramBuf, (char *)command.c_str());
4009     //# Go to http://msdn2.microsoft.com/en-us/library/ms682499.aspx
4010     //# to see how Win32 pipes work
4012     //# Create pipes
4013     SECURITY_ATTRIBUTES saAttr; 
4014     saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); 
4015     saAttr.bInheritHandle = TRUE; 
4016     saAttr.lpSecurityDescriptor = NULL; 
4017     HANDLE stdinRead,  stdinWrite;
4018     HANDLE stdoutRead, stdoutWrite;
4019     HANDLE stderrRead, stderrWrite;
4020     if (!CreatePipe(&stdinRead, &stdinWrite, &saAttr, 0))
4021         {
4022         error("executeProgram: could not create pipe");
4023         delete[] paramBuf;
4024         return false;
4025         } 
4026     SetHandleInformation(stdinWrite, HANDLE_FLAG_INHERIT, 0);
4027     if (!CreatePipe(&stdoutRead, &stdoutWrite, &saAttr, 0))
4028         {
4029         error("executeProgram: could not create pipe");
4030         delete[] paramBuf;
4031         return false;
4032         } 
4033     SetHandleInformation(stdoutRead, HANDLE_FLAG_INHERIT, 0);
4034     if (&outbuf != &errbuf) {
4035         if (!CreatePipe(&stderrRead, &stderrWrite, &saAttr, 0))
4036             {
4037             error("executeProgram: could not create pipe");
4038             delete[] paramBuf;
4039             return false;
4040             } 
4041         SetHandleInformation(stderrRead, HANDLE_FLAG_INHERIT, 0);
4042     } else {
4043         stderrRead = stdoutRead;
4044         stderrWrite = stdoutWrite;
4045     }
4047     // Create the process
4048     STARTUPINFO siStartupInfo;
4049     PROCESS_INFORMATION piProcessInfo;
4050     memset(&siStartupInfo, 0, sizeof(siStartupInfo));
4051     memset(&piProcessInfo, 0, sizeof(piProcessInfo));
4052     siStartupInfo.cb = sizeof(siStartupInfo);
4053     siStartupInfo.hStdError   =  stderrWrite;
4054     siStartupInfo.hStdOutput  =  stdoutWrite;
4055     siStartupInfo.hStdInput   =  stdinRead;
4056     siStartupInfo.dwFlags    |=  STARTF_USESTDHANDLES;
4057    
4058     if (!CreateProcess(NULL, paramBuf, NULL, NULL, true,
4059                 0, NULL, NULL, &siStartupInfo,
4060                 &piProcessInfo))
4061         {
4062         error("executeCommand : could not create process : %s",
4063                     win32LastError().c_str());
4064         ret = false;
4065         }
4067     delete[] paramBuf;
4069     DWORD bytesWritten;
4070     if (inbuf.size()>0 &&
4071         !WriteFile(stdinWrite, inbuf.c_str(), inbuf.size(), 
4072                &bytesWritten, NULL))
4073         {
4074         error("executeCommand: could not write to pipe");
4075         return false;
4076         }    
4077     if (!CloseHandle(stdinWrite))
4078         {          
4079         error("executeCommand: could not close write pipe");
4080         return false;
4081         }
4082     if (!CloseHandle(stdoutWrite))
4083         {
4084         error("executeCommand: could not close read pipe");
4085         return false;
4086         }
4087     if (stdoutWrite != stderrWrite && !CloseHandle(stderrWrite))
4088         {
4089         error("executeCommand: could not close read pipe");
4090         return false;
4091         }
4093     bool lastLoop = false;
4094     while (true)
4095         {
4096         DWORD avail;
4097         DWORD bytesRead;
4098         char readBuf[4096];
4100         //trace("## stderr");
4101         PeekNamedPipe(stderrRead, NULL, 0, NULL, &avail, NULL);
4102         if (avail > 0)
4103             {
4104             bytesRead = 0;
4105             if (avail>4096) avail = 4096;
4106             ReadFile(stderrRead, readBuf, avail, &bytesRead, NULL);
4107             if (bytesRead > 0)
4108                 {
4109                 for (unsigned int i=0 ; i<bytesRead ; i++)
4110                     errbuf.push_back(readBuf[i]);
4111                 }
4112             }
4114         //trace("## stdout");
4115         PeekNamedPipe(stdoutRead, NULL, 0, NULL, &avail, NULL);
4116         if (avail > 0)
4117             {
4118             bytesRead = 0;
4119             if (avail>4096) avail = 4096;
4120             ReadFile(stdoutRead, readBuf, avail, &bytesRead, NULL);
4121             if (bytesRead > 0)
4122                 {
4123                 for (unsigned int i=0 ; i<bytesRead ; i++)
4124                     outbuf.push_back(readBuf[i]);
4125                 }
4126             }
4127             
4128         //Was this the final check after program done?
4129         if (lastLoop)
4130             break;
4132         DWORD exitCode;
4133         GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
4134         if (exitCode != STILL_ACTIVE)
4135             lastLoop = true;
4137         Sleep(10);
4138         }    
4139     //trace("outbuf:%s", outbuf.c_str());
4140     if (!CloseHandle(stdoutRead))
4141         {
4142         error("executeCommand: could not close read pipe");
4143         return false;
4144         }
4145     if (stdoutRead != stderrRead && !CloseHandle(stderrRead))
4146         {
4147         error("executeCommand: could not close read pipe");
4148         return false;
4149         }
4151     DWORD exitCode;
4152     GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
4153     //trace("exit code:%d", exitCode);
4154     if (exitCode != 0)
4155         {
4156         ret = false;
4157         }
4158     
4159     CloseHandle(piProcessInfo.hProcess);
4160     CloseHandle(piProcessInfo.hThread);
4162     return ret;
4164
4166 #else  /*do it unix style*/
4168 #include <sys/wait.h>
4172 /**
4173  * Execute a system call, using pipes to send data to the
4174  * program's stdin,  and reading stdout and stderr.
4175  */
4176 bool MakeBase::executeCommand(const String &command,
4177                               const String &inbuf,
4178                               String &outbuf,
4179                               String &errbuf)
4182     status("============ cmd ============\n%s\n=============================",
4183                 command.c_str());
4185     outbuf.clear();
4186     errbuf.clear();
4187     
4189     int outfds[2];
4190     if (pipe(outfds) < 0)
4191         return false;
4192     int errfds[2];
4193     if (pipe(errfds) < 0)
4194         return false;
4195     int pid = fork();
4196     if (pid < 0)
4197         {
4198         close(outfds[0]);
4199         close(outfds[1]);
4200         close(errfds[0]);
4201         close(errfds[1]);
4202         error("launch of command '%s' failed : %s",
4203              command.c_str(), strerror(errno));
4204         return false;
4205         }
4206     else if (pid > 0) // parent
4207         {
4208         close(outfds[1]);
4209         close(errfds[1]);
4210         }
4211     else // == 0, child
4212         {
4213         close(outfds[0]);
4214         dup2(outfds[1], STDOUT_FILENO);
4215         close(outfds[1]);
4216         close(errfds[0]);
4217         dup2(errfds[1], STDERR_FILENO);
4218         close(errfds[1]);
4220         char *args[4];
4221         args[0] = (char *)"sh";
4222         args[1] = (char *)"-c";
4223         args[2] = (char *)command.c_str();
4224         args[3] = NULL;
4225         execv("/bin/sh", args);
4226         exit(EXIT_FAILURE);
4227         }
4229     String outb;
4230     String errb;
4232     int outRead = outfds[0];
4233     int errRead = errfds[0];
4234     int max = outRead;
4235     if (errRead > max)
4236         max = errRead;
4238     bool outOpen = true;
4239     bool errOpen = true;
4241     while (outOpen || errOpen)
4242         {
4243         char ch;
4244         fd_set fdset;
4245         FD_ZERO(&fdset);
4246         if (outOpen)
4247             FD_SET(outRead, &fdset);
4248         if (errOpen)
4249             FD_SET(errRead, &fdset);
4250         int ret = select(max+1, &fdset, NULL, NULL, NULL);
4251         if (ret < 0)
4252             break;
4253         if (FD_ISSET(outRead, &fdset))
4254             {
4255             if (read(outRead, &ch, 1) <= 0)
4256                 { outOpen = false; }
4257             else if (ch <= 0)
4258                 { /* outOpen = false; */ }
4259             else
4260                 { outb.push_back(ch); }
4261             }
4262         if (FD_ISSET(errRead, &fdset))
4263             {
4264             if (read(errRead, &ch, 1) <= 0)
4265                 { errOpen = false; }
4266             else if (ch <= 0)
4267                 { /* errOpen = false; */ }
4268             else
4269                 { errb.push_back(ch); }
4270             }
4271         }
4273     int childReturnValue;
4274     wait(&childReturnValue);
4276     close(outRead);
4277     close(errRead);
4279     outbuf = outb;
4280     errbuf = errb;
4282     if (childReturnValue != 0)
4283         {
4284         error("exec of command '%s' failed : %s",
4285              command.c_str(), strerror(childReturnValue));
4286         return false;
4287         }
4289     return true;
4290
4292 #endif
4297 bool MakeBase::listDirectories(const String &baseName,
4298                               const String &dirName,
4299                               std::vector<String> &res)
4301     res.push_back(dirName);
4302     String fullPath = baseName;
4303     if (dirName.size()>0)
4304         {
4305         if (dirName[0]!='/') fullPath.append("/");
4306         fullPath.append(dirName);
4307         }
4308     DIR *dir = opendir(fullPath.c_str());
4309     while (true)
4310         {
4311         struct dirent *de = readdir(dir);
4312         if (!de)
4313             break;
4315         //Get the directory member name
4316         String s = de->d_name;
4317         if (s.size() == 0 || s[0] == '.')
4318             continue;
4319         String childName = dirName;
4320         childName.append("/");
4321         childName.append(s);
4323         String fullChildPath = baseName;
4324         fullChildPath.append("/");
4325         fullChildPath.append(childName);
4326         struct stat finfo;
4327         String childNative = getNativePath(fullChildPath);
4328         if (cachedStat(childNative, &finfo)<0)
4329             {
4330             error("cannot stat file:%s", childNative.c_str());
4331             }
4332         else if (S_ISDIR(finfo.st_mode))
4333             {
4334             //trace("directory: %s", childName.c_str());
4335             if (!listDirectories(baseName, childName, res))
4336                 return false;
4337             }
4338         }
4339     closedir(dir);
4341     return true;
4345 bool MakeBase::listFiles(const String &baseDir,
4346                          const String &dirName,
4347                          std::vector<String> &res)
4349     String fullDir = baseDir;
4350     if (dirName.size()>0)
4351         {
4352         fullDir.append("/");
4353         fullDir.append(dirName);
4354         }
4355     String dirNative = getNativePath(fullDir);
4357     std::vector<String> subdirs;
4358     DIR *dir = opendir(dirNative.c_str());
4359     if (!dir)
4360         {
4361         error("Could not open directory %s : %s",
4362               dirNative.c_str(), strerror(errno));
4363         return false;
4364         }
4365     while (true)
4366         {
4367         struct dirent *de = readdir(dir);
4368         if (!de)
4369             break;
4371         //Get the directory member name
4372         String s = de->d_name;
4373         if (s.size() == 0 || s[0] == '.')
4374             continue;
4375         String childName;
4376         if (dirName.size()>0)
4377             {
4378             childName.append(dirName);
4379             childName.append("/");
4380             }
4381         childName.append(s);
4382         String fullChild = baseDir;
4383         fullChild.append("/");
4384         fullChild.append(childName);
4385         
4386         if (isDirectory(fullChild))
4387             {
4388             //trace("directory: %s", childName.c_str());
4389             if (!listFiles(baseDir, childName, res))
4390                 return false;
4391             continue;
4392             }
4393         else if (!isRegularFile(fullChild))
4394             {
4395             error("unknown file:%s", childName.c_str());
4396             return false;
4397             }
4399        //all done!
4400         res.push_back(childName);
4402         }
4403     closedir(dir);
4405     return true;
4409 /**
4410  * Several different classes extend MakeBase.  By "propRef", we mean
4411  * the one holding the properties.  Likely "Make" itself
4412  */
4413 bool MakeBase::listFiles(MakeBase &propRef, FileSet &fileSet)
4415     //before doing the list,  resolve any property references
4416     //that might have been specified in the directory name, such as ${src}
4417     String fsDir = fileSet.getDirectory();
4418     String dir;
4419     if (!propRef.getSubstitutions(fsDir, dir))
4420         return false;
4421     String baseDir = propRef.resolve(dir);
4422     std::vector<String> fileList;
4423     if (!listFiles(baseDir, "", fileList))
4424         return false;
4426     std::vector<String> includes = fileSet.getIncludes();
4427     std::vector<String> excludes = fileSet.getExcludes();
4429     std::vector<String> incs;
4430     std::vector<String>::iterator iter;
4432     std::sort(fileList.begin(), fileList.end());
4434     //If there are <includes>, then add files to the output
4435     //in the order of the include list
4436     if (includes.size()==0)
4437         incs = fileList;
4438     else
4439         {
4440         for (iter = includes.begin() ; iter != includes.end() ; iter++)
4441             {
4442             String &pattern = *iter;
4443             std::vector<String>::iterator siter;
4444             for (siter = fileList.begin() ; siter != fileList.end() ; siter++)
4445                 {
4446                 String s = *siter;
4447                 if (regexMatch(s, pattern))
4448                     {
4449                     //trace("INCLUDED:%s", s.c_str());
4450                     incs.push_back(s);
4451                     }
4452                 }
4453             }
4454         }
4456     //Now trim off the <excludes>
4457     std::vector<String> res;
4458     for (iter = incs.begin() ; iter != incs.end() ; iter++)
4459         {
4460         String s = *iter;
4461         bool skipme = false;
4462         std::vector<String>::iterator siter;
4463         for (siter = excludes.begin() ; siter != excludes.end() ; siter++)
4464             {
4465             String &pattern = *siter;
4466             if (regexMatch(s, pattern))
4467                 {
4468                 //trace("EXCLUDED:%s", s.c_str());
4469                 skipme = true;
4470                 break;
4471                 }
4472             }
4473         if (!skipme)
4474             res.push_back(s);
4475         }
4476         
4477     fileSet.setFiles(res);
4479     return true;
4483 /**
4484  * 0 == all, 1 = cflags, 2 = libs
4485  */ 
4486 bool MakeBase::pkgConfigRecursive(const String packageName,
4487                                   const String &path, 
4488                                   const String &prefix, 
4489                                   int query,
4490                                   String &result,
4491                                   std::set<String> &deplist) 
4493     PkgConfig pkgConfig;
4494     if (path.size() > 0)
4495         pkgConfig.setPath(path);
4496     if (prefix.size() > 0)
4497         pkgConfig.setPrefix(prefix);
4498     if (!pkgConfig.query(packageName))
4499         return false;
4500     if (query == 0)
4501         result = pkgConfig.getAll();
4502     else if (query == 1)
4503         result = pkgConfig.getCflags();
4504     else
4505         result = pkgConfig.getLibs();
4506     deplist.insert(packageName);
4507     std::vector<String> list = pkgConfig.getRequireList();
4508     for (unsigned int i = 0 ; i<list.size() ; i++)
4509         {
4510         String depPkgName = list[i];
4511         if (deplist.find(depPkgName) != deplist.end())
4512             continue;
4513         String val;
4514         if (!pkgConfigRecursive(depPkgName, path, prefix, query, val, deplist))
4515             {
4516             error("Based on 'requires' attribute of package '%s'", packageName.c_str());
4517             return false;
4518             }
4519         result.append(" ");
4520         result.append(val);
4521         }
4523     return true;
4526 bool MakeBase::pkgConfigQuery(const String &packageName, int query, String &result)
4528     std::set<String> deplist;
4529     String path = getProperty("pkg-config-path");
4530     if (path.size()>0)
4531         path = resolve(path);
4532     String prefix = getProperty("pkg-config-prefix");
4533     String val;
4534     if (!pkgConfigRecursive(packageName, path, prefix, query, val, deplist))
4535         return false;
4536     result = val;
4537     return true;
4542 /**
4543  * replace a variable ref like ${a} with a value
4544  */
4545 bool MakeBase::lookupProperty(const String &propertyName, String &result)
4547     String varname = propertyName;
4548     if (envPrefix.size() > 0 &&
4549         varname.compare(0, envPrefix.size(), envPrefix) == 0)
4550         {
4551         varname = varname.substr(envPrefix.size());
4552         char *envstr = getenv(varname.c_str());
4553         if (!envstr)
4554             {
4555             error("environment variable '%s' not defined", varname.c_str());
4556             return false;
4557             }
4558         result = envstr;
4559         }
4560     else if (pcPrefix.size() > 0 &&
4561         varname.compare(0, pcPrefix.size(), pcPrefix) == 0)
4562         {
4563         varname = varname.substr(pcPrefix.size());
4564         String val;
4565         if (!pkgConfigQuery(varname, 0, val))
4566             return false;
4567         result = val;
4568         }
4569     else if (pccPrefix.size() > 0 &&
4570         varname.compare(0, pccPrefix.size(), pccPrefix) == 0)
4571         {
4572         varname = varname.substr(pccPrefix.size());
4573         String val;
4574         if (!pkgConfigQuery(varname, 1, val))
4575             return false;
4576         result = val;
4577         }
4578     else if (pclPrefix.size() > 0 &&
4579         varname.compare(0, pclPrefix.size(), pclPrefix) == 0)
4580         {
4581         varname = varname.substr(pclPrefix.size());
4582         String val;
4583         if (!pkgConfigQuery(varname, 2, val))
4584             return false;
4585         result = val;
4586         }
4587     else
4588         {
4589         std::map<String, String>::iterator iter;
4590         iter = properties.find(varname);
4591         if (iter != properties.end())
4592             {
4593             result = iter->second;
4594             }
4595         else
4596             {
4597             error("property '%s' not found", varname.c_str());
4598             return false;
4599             }
4600         }
4601     return true;
4607 /**
4608  * Analyse a string, looking for any substitutions or other
4609  * things that need resolution 
4610  */
4611 bool MakeBase::getSubstitutionsRecursive(const String &str,
4612                                          String &result, int depth)
4614     if (depth > 10)
4615         {
4616         error("nesting of substitutions too deep (>10) for '%s'",
4617                         str.c_str());
4618         return false;
4619         }
4620     String s = trim(str);
4621     int len = (int)s.size();
4622     String val;
4623     for (int i=0 ; i<len ; i++)
4624         {
4625         char ch = s[i];
4626         if (ch == '$' && s[i+1] == '{')
4627             {
4628             String varname;
4629             int j = i+2;
4630             for ( ; j<len ; j++)
4631                 {
4632                 ch = s[j];
4633                 if (ch == '$' && s[j+1] == '{')
4634                     {
4635                     error("attribute %s cannot have nested variable references",
4636                            s.c_str());
4637                     return false;
4638                     }
4639                 else if (ch == '}')
4640                     {
4641                     varname = trim(varname);
4642                     String varval;
4643                     if (!lookupProperty(varname, varval))
4644                         return false;
4645                     String varval2;
4646                     //Now see if the answer has ${} in it, too
4647                     if (!getSubstitutionsRecursive(varval, varval2, depth + 1))
4648                         return false;
4649                     val.append(varval2);
4650                     break;
4651                     }
4652                 else
4653                     {
4654                     varname.push_back(ch);
4655                     }
4656                 }
4657             i = j;
4658             }
4659         else
4660             {
4661             val.push_back(ch);
4662             }
4663         }
4664     result = val;
4665     return true;
4668 /**
4669  * Analyse a string, looking for any substitutions or other
4670  * things that need resilution 
4671  */
4672 bool MakeBase::getSubstitutions(const String &str, String &result)
4674     return getSubstitutionsRecursive(str, result, 0);
4679 /**
4680  * replace variable refs like ${a} with their values
4681  * Assume that the string has already been syntax validated
4682  */
4683 String MakeBase::eval(const String &s, const String &defaultVal)
4685     if (s.size()==0)
4686         return defaultVal;
4687     String ret;
4688     if (getSubstitutions(s, ret))
4689         return ret;
4690     else
4691         return defaultVal;
4695 /**
4696  * replace variable refs like ${a} with their values
4697  * return true or false
4698  * Assume that the string has already been syntax validated
4699  */
4700 bool MakeBase::evalBool(const String &s, bool defaultVal)
4702     if (s.size()==0)
4703         return defaultVal;
4704     String val = eval(s, "false");
4705     if (val.size()==0)
4706         return defaultVal;
4707     if (val == "true" || val == "TRUE")
4708         return true;
4709     else
4710         return false;
4714 /**
4715  * Get a string attribute, testing it for proper syntax and
4716  * property names.
4717  */
4718 bool MakeBase::getAttribute(Element *elem, const String &name,
4719                                     String &result)
4721     String s = elem->getAttribute(name);
4722     String tmp;
4723     bool ret = getSubstitutions(s, tmp);
4724     if (ret)
4725         result = s;  //assign -if- ok
4726     return ret;
4730 /**
4731  * Get a string value, testing it for proper syntax and
4732  * property names.
4733  */
4734 bool MakeBase::getValue(Element *elem, String &result)
4736     String s = elem->getValue();
4737     String tmp;
4738     bool ret = getSubstitutions(s, tmp);
4739     if (ret)
4740         result = s;  //assign -if- ok
4741     return ret;
4747 /**
4748  * Parse a <patternset> entry
4749  */  
4750 bool MakeBase::parsePatternSet(Element *elem,
4751                           MakeBase &propRef,
4752                           std::vector<String> &includes,
4753                           std::vector<String> &excludes
4754                           )
4756     std::vector<Element *> children  = elem->getChildren();
4757     for (unsigned int i=0 ; i<children.size() ; i++)
4758         {
4759         Element *child = children[i];
4760         String tagName = child->getName();
4761         if (tagName == "exclude")
4762             {
4763             String fname;
4764             if (!propRef.getAttribute(child, "name", fname))
4765                 return false;
4766             //trace("EXCLUDE: %s", fname.c_str());
4767             excludes.push_back(fname);
4768             }
4769         else if (tagName == "include")
4770             {
4771             String fname;
4772             if (!propRef.getAttribute(child, "name", fname))
4773                 return false;
4774             //trace("INCLUDE: %s", fname.c_str());
4775             includes.push_back(fname);
4776             }
4777         }
4779     return true;
4785 /**
4786  * Parse a <fileset> entry, and determine which files
4787  * should be included
4788  */  
4789 bool MakeBase::parseFileSet(Element *elem,
4790                           MakeBase &propRef,
4791                           FileSet &fileSet)
4793     String name = elem->getName();
4794     if (name != "fileset")
4795         {
4796         error("expected <fileset>");
4797         return false;
4798         }
4801     std::vector<String> includes;
4802     std::vector<String> excludes;
4804     //A fileset has one implied patternset
4805     if (!parsePatternSet(elem, propRef, includes, excludes))
4806         {
4807         return false;
4808         }
4809     //Look for child tags, including more patternsets
4810     std::vector<Element *> children  = elem->getChildren();
4811     for (unsigned int i=0 ; i<children.size() ; i++)
4812         {
4813         Element *child = children[i];
4814         String tagName = child->getName();
4815         if (tagName == "patternset")
4816             {
4817             if (!parsePatternSet(child, propRef, includes, excludes))
4818                 {
4819                 return false;
4820                 }
4821             }
4822         }
4824     String dir;
4825     //Now do the stuff
4826     //Get the base directory for reading file names
4827     if (!propRef.getAttribute(elem, "dir", dir))
4828         return false;
4830     fileSet.setDirectory(dir);
4831     fileSet.setIncludes(includes);
4832     fileSet.setExcludes(excludes);
4833     
4834     /*
4835     std::vector<String> fileList;
4836     if (dir.size() > 0)
4837         {
4838         String baseDir = propRef.resolve(dir);
4839         if (!listFiles(baseDir, "", includes, excludes, fileList))
4840             return false;
4841         }
4842     std::sort(fileList.begin(), fileList.end());
4843     result = fileList;
4844     */
4846     
4847     /*
4848     for (unsigned int i=0 ; i<result.size() ; i++)
4849         {
4850         trace("RES:%s", result[i].c_str());
4851         }
4852     */
4854     
4855     return true;
4858 /**
4859  * Parse a <filelist> entry.  This is far simpler than FileSet,
4860  * since no directory scanning is needed.  The file names are listed
4861  * explicitly.
4862  */  
4863 bool MakeBase::parseFileList(Element *elem,
4864                           MakeBase &propRef,
4865                           FileList &fileList)
4867     std::vector<String> fnames;
4868     //Look for child tags, namely "file"
4869     std::vector<Element *> children  = elem->getChildren();
4870     for (unsigned int i=0 ; i<children.size() ; i++)
4871         {
4872         Element *child = children[i];
4873         String tagName = child->getName();
4874         if (tagName == "file")
4875             {
4876             String fname = child->getAttribute("name");
4877             if (fname.size()==0)
4878                 {
4879                 error("<file> element requires name="" attribute");
4880                 return false;
4881                 }
4882             fnames.push_back(fname);
4883             }
4884         else
4885             {
4886             error("tag <%s> not allowed in <fileset>", tagName.c_str());
4887             return false;
4888             }
4889         }
4891     String dir;
4892     //Get the base directory for reading file names
4893     if (!propRef.getAttribute(elem, "dir", dir))
4894         return false;
4895     fileList.setDirectory(dir);
4896     fileList.setFiles(fnames);
4898     return true;
4903 /**
4904  * Create a directory, making intermediate dirs
4905  * if necessary
4906  */                  
4907 bool MakeBase::createDirectory(const String &dirname)
4909     //trace("## createDirectory: %s", dirname.c_str());
4910     //## first check if it exists
4911     struct stat finfo;
4912     String nativeDir = getNativePath(dirname);
4913     char *cnative = (char *) nativeDir.c_str();
4914 #ifdef __WIN32__
4915     if (strlen(cnative)==2 && cnative[1]==':')
4916         return true;
4917 #endif
4918     if (cachedStat(nativeDir, &finfo)==0)
4919         {
4920         if (!S_ISDIR(finfo.st_mode))
4921             {
4922             error("mkdir: file %s exists but is not a directory",
4923                   cnative);
4924             return false;
4925             }
4926         else //exists
4927             {
4928             return true;
4929             }
4930         }
4932     //## 2: pull off the last path segment, if any,
4933     //## to make the dir 'above' this one, if necessary
4934     unsigned int pos = dirname.find_last_of('/');
4935     if (pos>0 && pos != dirname.npos)
4936         {
4937         String subpath = dirname.substr(0, pos);
4938         //A letter root (c:) ?
4939         if (!createDirectory(subpath))
4940             return false;
4941         }
4942         
4943     //## 3: now make
4944 #ifdef __WIN32__
4945     if (mkdir(cnative)<0)
4946 #else
4947     if (mkdir(cnative, S_IRWXU | S_IRWXG | S_IRWXO)<0)
4948 #endif
4949         {
4950         error("cannot make directory '%s' : %s",
4951                  cnative, strerror(errno));
4952         return false;
4953         }
4955     removeFromStatCache(nativeDir);
4956         
4957     return true;
4961 /**
4962  * Remove a directory recursively
4963  */ 
4964 bool MakeBase::removeDirectory(const String &dirName)
4966     char *dname = (char *)dirName.c_str();
4968     DIR *dir = opendir(dname);
4969     if (!dir)
4970         {
4971         //# Let this fail nicely.
4972         return true;
4973         //error("error opening directory %s : %s", dname, strerror(errno));
4974         //return false;
4975         }
4976     
4977     while (true)
4978         {
4979         struct dirent *de = readdir(dir);
4980         if (!de)
4981             break;
4983         //Get the directory member name
4984         String s = de->d_name;
4985         if (s.size() == 0 || s[0] == '.')
4986             continue;
4987         String childName;
4988         if (dirName.size() > 0)
4989             {
4990             childName.append(dirName);
4991             childName.append("/");
4992             }
4993         childName.append(s);
4996         struct stat finfo;
4997         String childNative = getNativePath(childName);
4998         char *cnative = (char *)childNative.c_str();
4999         if (cachedStat(childNative, &finfo)<0)
5000             {
5001             error("cannot stat file:%s", cnative);
5002             }
5003         else if (S_ISDIR(finfo.st_mode))
5004             {
5005             //trace("DEL dir: %s", childName.c_str());
5006             if (!removeDirectory(childName))
5007                 {
5008                 return false;
5009                 }
5010             }
5011         else if (!S_ISREG(finfo.st_mode))
5012             {
5013             //trace("not regular: %s", cnative);
5014             }
5015         else
5016             {
5017             //trace("DEL file: %s", childName.c_str());
5018             if (!removeFile(childName))
5019                 {
5020                 return false;
5021                 }
5022             }
5023         }
5024     closedir(dir);
5026     //Now delete the directory
5027     String native = getNativePath(dirName);
5028     if (rmdir(native.c_str())<0)
5029         {
5030         error("could not delete directory %s : %s",
5031             native.c_str() , strerror(errno));
5032         return false;
5033         }
5035     removeFromStatCache(native);
5037     return true;
5038     
5042 /**
5043  * Copy a file from one name to another. Perform only if needed
5044  */ 
5045 bool MakeBase::copyFile(const String &srcFile, const String &destFile)
5047     //# 1 Check up-to-date times
5048     String srcNative = getNativePath(srcFile);
5049     struct stat srcinfo;
5050     if (cachedStat(srcNative, &srcinfo)<0)
5051         {
5052         error("source file %s for copy does not exist",
5053                  srcNative.c_str());
5054         return false;
5055         }
5057     String destNative = getNativePath(destFile);
5058     struct stat destinfo;
5059     if (cachedStat(destNative, &destinfo)==0)
5060         {
5061         if (destinfo.st_mtime >= srcinfo.st_mtime)
5062             return true;
5063         }
5064         
5065     //# 2 prepare a destination directory if necessary
5066     unsigned int pos = destFile.find_last_of('/');
5067     if (pos != destFile.npos)
5068         {
5069         String subpath = destFile.substr(0, pos);
5070         if (!createDirectory(subpath))
5071             return false;
5072         }
5074     //# 3 do the data copy
5075 #ifndef __WIN32__
5077     FILE *srcf = fopen(srcNative.c_str(), "rb");
5078     if (!srcf)
5079         {
5080         error("copyFile cannot open '%s' for reading", srcNative.c_str());
5081         return false;
5082         }
5083     FILE *destf = fopen(destNative.c_str(), "wb");
5084     if (!destf)
5085         {
5086         error("copyFile cannot open %s for writing", srcNative.c_str());
5087         return false;
5088         }
5090     while (!feof(srcf))
5091         {
5092         int ch = fgetc(srcf);
5093         if (ch<0)
5094             break;
5095         fputc(ch, destf);
5096         }
5098     fclose(destf);
5099     fclose(srcf);
5101 #else
5102     
5103     if (!CopyFile(srcNative.c_str(), destNative.c_str(), false))
5104         {
5105         error("copyFile from %s to %s failed",
5106              srcNative.c_str(), destNative.c_str());
5107         return false;
5108         }
5109         
5110 #endif /* __WIN32__ */
5112     removeFromStatCache(destNative);
5114     return true;
5118 /**
5119  * Delete a file
5120  */ 
5121 bool MakeBase::removeFile(const String &file)
5123     String native = getNativePath(file);
5125     if (!fileExists(native))
5126         {
5127         return true;
5128         }
5130 #ifdef WIN32
5131     // On Windows 'remove' will only delete files
5133     if (remove(native.c_str())<0)
5134         {
5135         if (errno==EACCES)
5136             {
5137             error("File %s is read-only", native.c_str());
5138             }
5139         else if (errno==ENOENT)
5140             {
5141             error("File %s does not exist or is a directory", native.c_str());
5142             }
5143         else
5144             {
5145             error("Failed to delete file %s: %s", native.c_str(), strerror(errno));
5146             }
5147         return false;
5148         }
5150 #else
5152     if (!isRegularFile(native))
5153         {
5154         error("File %s does not exist or is not a regular file", native.c_str());
5155         return false;
5156         }
5158     if (remove(native.c_str())<0)
5159         {
5160         if (errno==EACCES)
5161             {
5162             error("File %s is read-only", native.c_str());
5163             }
5164         else
5165             {
5166             error(
5167                 errno==EACCES ? "File %s is read-only" :
5168                 errno==ENOENT ? "File %s does not exist or is a directory" :
5169                 "Failed to delete file %s: %s", native.c_str());
5170             }
5171         return false;
5172         }
5174 #endif
5176     removeFromStatCache(native);
5178     return true;
5182 /**
5183  * Tests if the file exists
5184  */ 
5185 bool MakeBase::fileExists(const String &fileName)
5187     String native = getNativePath(fileName);
5188     struct stat finfo;
5189     
5190     //Exists?
5191     if (cachedStat(native, &finfo)<0)
5192         return false;
5194     return true;
5198 /**
5199  * Tests if the file exists and is a regular file
5200  */ 
5201 bool MakeBase::isRegularFile(const String &fileName)
5203     String native = getNativePath(fileName);
5204     struct stat finfo;
5205     
5206     //Exists?
5207     if (cachedStat(native, &finfo)<0)
5208         return false;
5211     //check the file mode
5212     if (!S_ISREG(finfo.st_mode))
5213         return false;
5215     return true;
5218 /**
5219  * Tests if the file exists and is a directory
5220  */ 
5221 bool MakeBase::isDirectory(const String &fileName)
5223     String native = getNativePath(fileName);
5224     struct stat finfo;
5225     
5226     //Exists?
5227     if (cachedStat(native, &finfo)<0)
5228         return false;
5231     //check the file mode
5232     if (!S_ISDIR(finfo.st_mode))
5233         return false;
5235     return true;
5240 /**
5241  * Tests is the modification of fileA is newer than fileB
5242  */ 
5243 bool MakeBase::isNewerThan(const String &fileA, const String &fileB)
5245     //trace("isNewerThan:'%s' , '%s'", fileA.c_str(), fileB.c_str());
5246     String nativeA = getNativePath(fileA);
5247     struct stat infoA;
5248     //IF source does not exist, NOT newer
5249     if (cachedStat(nativeA, &infoA)<0)
5250         {
5251         return false;
5252         }
5254     String nativeB = getNativePath(fileB);
5255     struct stat infoB;
5256     //IF dest does not exist, YES, newer
5257     if (cachedStat(nativeB, &infoB)<0)
5258         {
5259         return true;
5260         }
5262     //check the actual times
5263     if (infoA.st_mtime > infoB.st_mtime)
5264         {
5265         return true;
5266         }
5268     return false;
5272 //########################################################################
5273 //# P K G    C O N F I G
5274 //########################################################################
5277 /**
5278  * Get a character from the buffer at pos.  If out of range,
5279  * return -1 for safety
5280  */
5281 int PkgConfig::get(int pos)
5283     if (pos>parselen)
5284         return -1;
5285     return parsebuf[pos];
5290 /**
5291  *  Skip over all whitespace characters beginning at pos.  Return
5292  *  the position of the first non-whitespace character.
5293  *  Pkg-config is line-oriented, so check for newline
5294  */
5295 int PkgConfig::skipwhite(int pos)
5297     while (pos < parselen)
5298         {
5299         int ch = get(pos);
5300         if (ch < 0)
5301             break;
5302         if (!isspace(ch))
5303             break;
5304         pos++;
5305         }
5306     return pos;
5310 /**
5311  *  Parse the buffer beginning at pos, for a word.  Fill
5312  *  'ret' with the result.  Return the position after the
5313  *  word.
5314  */
5315 int PkgConfig::getword(int pos, String &ret)
5317     while (pos < parselen)
5318         {
5319         int ch = get(pos);
5320         if (ch < 0)
5321             break;
5322         if (!isalnum(ch) && ch != '_' && ch != '-' && ch != '+' && ch != '.')
5323             break;
5324         ret.push_back((char)ch);
5325         pos++;
5326         }
5327     return pos;
5330 bool PkgConfig::parseRequires()
5332     if (requires.size() == 0)
5333         return true;
5334     parsebuf = (char *)requires.c_str();
5335     parselen = requires.size();
5336     int pos = 0;
5337     while (pos < parselen)
5338         {
5339         pos = skipwhite(pos);
5340         String val;
5341         int pos2 = getword(pos, val);
5342         if (pos2 == pos)
5343             break;
5344         pos = pos2;
5345         //trace("val %s", val.c_str());
5346         requireList.push_back(val);
5347         }
5348     return true;
5352 static int getint(const String str)
5354     char *s = (char *)str.c_str();
5355     char *ends = NULL;
5356     long val = strtol(s, &ends, 10);
5357     if (ends == s)
5358         return 0L;
5359     else
5360         return val;
5363 void PkgConfig::parseVersion()
5365     if (version.size() == 0)
5366         return;
5367     String s1, s2, s3;
5368     unsigned int pos = 0;
5369     unsigned int pos2 = version.find('.', pos);
5370     if (pos2 == version.npos)
5371         {
5372         s1 = version;
5373         }
5374     else
5375         {
5376         s1 = version.substr(pos, pos2-pos);
5377         pos = pos2;
5378         pos++;
5379         if (pos < version.size())
5380             {
5381             pos2 = version.find('.', pos);
5382             if (pos2 == version.npos)
5383                 {
5384                 s2 = version.substr(pos, version.size()-pos);
5385                 }
5386             else
5387                 {
5388                 s2 = version.substr(pos, pos2-pos);
5389                 pos = pos2;
5390                 pos++;
5391                 if (pos < version.size())
5392                     s3 = version.substr(pos, pos2-pos);
5393                 }
5394             }
5395         }
5397     majorVersion = getint(s1);
5398     minorVersion = getint(s2);
5399     microVersion = getint(s3);
5400     //trace("version:%d.%d.%d", majorVersion,
5401     //          minorVersion, microVersion );
5405 bool PkgConfig::parseLine(const String &lineBuf)
5407     parsebuf = (char *)lineBuf.c_str();
5408     parselen = lineBuf.size();
5409     int pos = 0;
5410     
5411     while (pos < parselen)
5412         {
5413         String attrName;
5414         pos = skipwhite(pos);
5415         int ch = get(pos);
5416         if (ch == '#')
5417             {
5418             //comment.  eat the rest of the line
5419             while (pos < parselen)
5420                 {
5421                 ch = get(pos);
5422                 if (ch == '\n' || ch < 0)
5423                     break;
5424                 pos++;
5425                 }
5426             continue;
5427             }
5428         pos = getword(pos, attrName);
5429         if (attrName.size() == 0)
5430             continue;
5431         
5432         pos = skipwhite(pos);
5433         ch = get(pos);
5434         if (ch != ':' && ch != '=')
5435             {
5436             error("expected ':' or '='");
5437             return false;
5438             }
5439         pos++;
5440         pos = skipwhite(pos);
5441         String attrVal;
5442         while (pos < parselen)
5443             {
5444             ch = get(pos);
5445             if (ch == '\n' || ch < 0)
5446                 break;
5447             else if (ch == '$' && get(pos+1) == '{')
5448                 {
5449                 //#  this is a ${substitution}
5450                 pos += 2;
5451                 String subName;
5452                 while (pos < parselen)
5453                     {
5454                     ch = get(pos);
5455                     if (ch < 0)
5456                         {
5457                         error("unterminated substitution");
5458                         return false;
5459                         }
5460                     else if (ch == '}')
5461                         break;
5462                     else
5463                         subName.push_back((char)ch);
5464                     pos++;
5465                     }
5466                 //trace("subName:%s %s", subName.c_str(), prefix.c_str());
5467                 if (subName == "prefix" && prefix.size()>0)
5468                     {
5469                     attrVal.append(prefix);
5470                     //trace("prefix override:%s", prefix.c_str());
5471                     }
5472                 else
5473                     {
5474                     String subVal = attrs[subName];
5475                     //trace("subVal:%s", subVal.c_str());
5476                     attrVal.append(subVal);
5477                     }
5478                 }
5479             else
5480                 attrVal.push_back((char)ch);
5481             pos++;
5482             }
5484         attrVal = trim(attrVal);
5485         attrs[attrName] = attrVal;
5487         String attrNameL = toLower(attrName);
5489         if (attrNameL == "name")
5490             name = attrVal;
5491         else if (attrNameL == "description")
5492             description = attrVal;
5493         else if (attrNameL == "cflags")
5494             cflags = attrVal;
5495         else if (attrNameL == "libs")
5496             libs = attrVal;
5497         else if (attrNameL == "requires")
5498             requires = attrVal;
5499         else if (attrNameL == "version")
5500             version = attrVal;
5502         //trace("name:'%s'  value:'%s'",
5503         //      attrName.c_str(), attrVal.c_str());
5504         }
5506     return true;
5510 bool PkgConfig::parse(const String &buf)
5512     init();
5514     String line;
5515     int lineNr = 0;
5516     for (unsigned int p=0 ; p<buf.size() ; p++)
5517         {
5518         int ch = buf[p];
5519         if (ch == '\n' || ch == '\r')
5520             {
5521             if (!parseLine(line))
5522                 return false;
5523             line.clear();
5524             lineNr++;
5525             }
5526         else
5527             {
5528             line.push_back(ch);
5529             }
5530         }
5531     if (line.size()>0)
5532         {
5533         if (!parseLine(line))
5534             return false;
5535         }
5537     parseRequires();
5538     parseVersion();
5540     return true;
5546 void PkgConfig::dumpAttrs()
5548     //trace("### PkgConfig attributes for %s", fileName.c_str());
5549     std::map<String, String>::iterator iter;
5550     for (iter=attrs.begin() ; iter!=attrs.end() ; iter++)
5551         {
5552         trace("   %s = %s", iter->first.c_str(), iter->second.c_str());
5553         }
5557 bool PkgConfig::readFile(const String &fname)
5559     fileName = getNativePath(fname);
5561     FILE *f = fopen(fileName.c_str(), "r");
5562     if (!f)
5563         {
5564         error("cannot open file '%s' for reading", fileName.c_str());
5565         return false;
5566         }
5567     String buf;
5568     while (true)
5569         {
5570         int ch = fgetc(f);
5571         if (ch < 0)
5572             break;
5573         buf.push_back((char)ch);
5574         }
5575     fclose(f);
5577     //trace("####### File:\n%s", buf.c_str());
5578     if (!parse(buf))
5579         {
5580         return false;
5581         }
5583     //dumpAttrs();
5585     return true;
5590 bool PkgConfig::query(const String &pkgName)
5592     name = pkgName;
5594     String fname = path;
5595     fname.append("/");
5596     fname.append(name);
5597     fname.append(".pc");
5599     if (!readFile(fname))
5600         {
5601         error("Cannot find package '%s'. Do you have it installed?",
5602                        pkgName.c_str());
5603         return false;
5604         }
5605     
5606     return true;
5613 //########################################################################
5614 //# D E P T O O L
5615 //########################################################################
5619 /**
5620  *  Class which holds information for each file.
5621  */
5622 class FileRec
5624 public:
5626     typedef enum
5627         {
5628         UNKNOWN,
5629         CFILE,
5630         HFILE,
5631         OFILE
5632         } FileType;
5634     /**
5635      *  Constructor
5636      */
5637     FileRec()
5638         { init(); type = UNKNOWN; }
5640     /**
5641      *  Copy constructor
5642      */
5643     FileRec(const FileRec &other)
5644         { init(); assign(other); }
5645     /**
5646      *  Constructor
5647      */
5648     FileRec(int typeVal)
5649         { init(); type = typeVal; }
5650     /**
5651      *  Assignment operator
5652      */
5653     FileRec &operator=(const FileRec &other)
5654         { init(); assign(other); return *this; }
5657     /**
5658      *  Destructor
5659      */
5660     ~FileRec()
5661         {}
5663     /**
5664      *  Directory part of the file name
5665      */
5666     String path;
5668     /**
5669      *  Base name, sans directory and suffix
5670      */
5671     String baseName;
5673     /**
5674      *  File extension, such as cpp or h
5675      */
5676     String suffix;
5678     /**
5679      *  Type of file: CFILE, HFILE, OFILE
5680      */
5681     int type;
5683     /**
5684      * Used to list files ref'd by this one
5685      */
5686     std::map<String, FileRec *> files;
5689 private:
5691     void init()
5692         {
5693         }
5695     void assign(const FileRec &other)
5696         {
5697         type     = other.type;
5698         baseName = other.baseName;
5699         suffix   = other.suffix;
5700         files    = other.files;
5701         }
5703 };
5707 /**
5708  *  Simpler dependency record
5709  */
5710 class DepRec
5712 public:
5714     /**
5715      *  Constructor
5716      */
5717     DepRec()
5718         {init();}
5720     /**
5721      *  Copy constructor
5722      */
5723     DepRec(const DepRec &other)
5724         {init(); assign(other);}
5725     /**
5726      *  Constructor
5727      */
5728     DepRec(const String &fname)
5729         {init(); name = fname; }
5730     /**
5731      *  Assignment operator
5732      */
5733     DepRec &operator=(const DepRec &other)
5734         {init(); assign(other); return *this;}
5737     /**
5738      *  Destructor
5739      */
5740     ~DepRec()
5741         {}
5743     /**
5744      *  Directory part of the file name
5745      */
5746     String path;
5748     /**
5749      *  Base name, without the path and suffix
5750      */
5751     String name;
5753     /**
5754      *  Suffix of the source
5755      */
5756     String suffix;
5759     /**
5760      * Used to list files ref'd by this one
5761      */
5762     std::vector<String> files;
5765 private:
5767     void init()
5768         {
5769         }
5771     void assign(const DepRec &other)
5772         {
5773         path     = other.path;
5774         name     = other.name;
5775         suffix   = other.suffix;
5776         files    = other.files; //avoid recursion
5777         }
5779 };
5782 class DepTool : public MakeBase
5784 public:
5786     /**
5787      *  Constructor
5788      */
5789     DepTool()
5790         { init(); }
5792     /**
5793      *  Copy constructor
5794      */
5795     DepTool(const DepTool &other)
5796         { init(); assign(other); }
5798     /**
5799      *  Assignment operator
5800      */
5801     DepTool &operator=(const DepTool &other)
5802         { init(); assign(other); return *this; }
5805     /**
5806      *  Destructor
5807      */
5808     ~DepTool()
5809         {}
5812     /**
5813      *  Reset this section of code
5814      */
5815     virtual void init();
5816     
5817     /**
5818      *  Reset this section of code
5819      */
5820     virtual void assign(const DepTool &other)
5821         {
5822         }
5823     
5824     /**
5825      *  Sets the source directory which will be scanned
5826      */
5827     virtual void setSourceDirectory(const String &val)
5828         { sourceDir = val; }
5830     /**
5831      *  Returns the source directory which will be scanned
5832      */
5833     virtual String getSourceDirectory()
5834         { return sourceDir; }
5836     /**
5837      *  Sets the list of files within the directory to analyze
5838      */
5839     virtual void setFileList(const std::vector<String> &list)
5840         { fileList = list; }
5842     /**
5843      * Creates the list of all file names which will be
5844      * candidates for further processing.  Reads make.exclude
5845      * to see which files for directories to leave out.
5846      */
5847     virtual bool createFileList();
5850     /**
5851      *  Generates the forward dependency list
5852      */
5853     virtual bool generateDependencies();
5856     /**
5857      *  Generates the forward dependency list, saving the file
5858      */
5859     virtual bool generateDependencies(const String &);
5862     /**
5863      *  Load a dependency file
5864      */
5865     std::vector<DepRec> loadDepFile(const String &fileName);
5867     /**
5868      *  Load a dependency file, generating one if necessary
5869      */
5870     std::vector<DepRec> getDepFile(const String &fileName,
5871               bool forceRefresh);
5873     /**
5874      *  Save a dependency file
5875      */
5876     bool saveDepFile(const String &fileName);
5879 private:
5882     /**
5883      *
5884      */
5885     void parseName(const String &fullname,
5886                    String &path,
5887                    String &basename,
5888                    String &suffix);
5890     /**
5891      *
5892      */
5893     int get(int pos);
5895     /**
5896      *
5897      */
5898     int skipwhite(int pos);
5900     /**
5901      *
5902      */
5903     int getword(int pos, String &ret);
5905     /**
5906      *
5907      */
5908     bool sequ(int pos, const char *key);
5910     /**
5911      *
5912      */
5913     bool addIncludeFile(FileRec *frec, const String &fname);
5915     /**
5916      *
5917      */
5918     bool scanFile(const String &fname, FileRec *frec);
5920     /**
5921      *
5922      */
5923     bool processDependency(FileRec *ofile, FileRec *include);
5925     /**
5926      *
5927      */
5928     String sourceDir;
5930     /**
5931      *
5932      */
5933     std::vector<String> fileList;
5935     /**
5936      *
5937      */
5938     std::vector<String> directories;
5940     /**
5941      * A list of all files which will be processed for
5942      * dependencies.
5943      */
5944     std::map<String, FileRec *> allFiles;
5946     /**
5947      * The list of .o files, and the
5948      * dependencies upon them.
5949      */
5950     std::map<String, FileRec *> oFiles;
5952     int depFileSize;
5953     char *depFileBuf;
5955     static const int readBufSize = 8192;
5956     char readBuf[8193];//byte larger
5958 };
5964 /**
5965  *  Clean up after processing.  Called by the destructor, but should
5966  *  also be called before the object is reused.
5967  */
5968 void DepTool::init()
5970     sourceDir = ".";
5972     fileList.clear();
5973     directories.clear();
5974     
5975     //clear output file list
5976     std::map<String, FileRec *>::iterator iter;
5977     for (iter=oFiles.begin(); iter!=oFiles.end() ; iter++)
5978         delete iter->second;
5979     oFiles.clear();
5981     //allFiles actually contains the master copies. delete them
5982     for (iter= allFiles.begin(); iter!=allFiles.end() ; iter++)
5983         delete iter->second;
5984     allFiles.clear(); 
5991 /**
5992  *  Parse a full path name into path, base name, and suffix
5993  */
5994 void DepTool::parseName(const String &fullname,
5995                         String &path,
5996                         String &basename,
5997                         String &suffix)
5999     if (fullname.size() < 2)
6000         return;
6002     unsigned int pos = fullname.find_last_of('/');
6003     if (pos != fullname.npos && pos<fullname.size()-1)
6004         {
6005         path = fullname.substr(0, pos);
6006         pos++;
6007         basename = fullname.substr(pos, fullname.size()-pos);
6008         }
6009     else
6010         {
6011         path = "";
6012         basename = fullname;
6013         }
6015     pos = basename.find_last_of('.');
6016     if (pos != basename.npos && pos<basename.size()-1)
6017         {
6018         suffix   = basename.substr(pos+1, basename.size()-pos-1);
6019         basename = basename.substr(0, pos);
6020         }
6022     //trace("parsename:%s %s %s", path.c_str(),
6023     //        basename.c_str(), suffix.c_str()); 
6028 /**
6029  *  Generate our internal file list.
6030  */
6031 bool DepTool::createFileList()
6034     for (unsigned int i=0 ; i<fileList.size() ; i++)
6035         {
6036         String fileName = fileList[i];
6037         //trace("## FileName:%s", fileName.c_str());
6038         String path;
6039         String basename;
6040         String sfx;
6041         parseName(fileName, path, basename, sfx);
6042         if (sfx == "cpp" || sfx == "c" || sfx == "cxx"   ||
6043             sfx == "cc" || sfx == "CC")
6044             {
6045             FileRec *fe         = new FileRec(FileRec::CFILE);
6046             fe->path            = path;
6047             fe->baseName        = basename;
6048             fe->suffix          = sfx;
6049             allFiles[fileName]  = fe;
6050             }
6051         else if (sfx == "h"   ||  sfx == "hh"  ||
6052                  sfx == "hpp" ||  sfx == "hxx")
6053             {
6054             FileRec *fe         = new FileRec(FileRec::HFILE);
6055             fe->path            = path;
6056             fe->baseName        = basename;
6057             fe->suffix          = sfx;
6058             allFiles[fileName]  = fe;
6059             }
6060         }
6062     if (!listDirectories(sourceDir, "", directories))
6063         return false;
6064         
6065     return true;
6072 /**
6073  * Get a character from the buffer at pos.  If out of range,
6074  * return -1 for safety
6075  */
6076 int DepTool::get(int pos)
6078     if (pos>depFileSize)
6079         return -1;
6080     return depFileBuf[pos];
6085 /**
6086  *  Skip over all whitespace characters beginning at pos.  Return
6087  *  the position of the first non-whitespace character.
6088  */
6089 int DepTool::skipwhite(int pos)
6091     while (pos < depFileSize)
6092         {
6093         int ch = get(pos);
6094         if (ch < 0)
6095             break;
6096         if (!isspace(ch))
6097             break;
6098         pos++;
6099         }
6100     return pos;
6104 /**
6105  *  Parse the buffer beginning at pos, for a word.  Fill
6106  *  'ret' with the result.  Return the position after the
6107  *  word.
6108  */
6109 int DepTool::getword(int pos, String &ret)
6111     while (pos < depFileSize)
6112         {
6113         int ch = get(pos);
6114         if (ch < 0)
6115             break;
6116         if (isspace(ch))
6117             break;
6118         ret.push_back((char)ch);
6119         pos++;
6120         }
6121     return pos;
6124 /**
6125  * Return whether the sequence of characters in the buffer
6126  * beginning at pos match the key,  for the length of the key
6127  */
6128 bool DepTool::sequ(int pos, const char *key)
6130     while (*key)
6131         {
6132         if (*key != get(pos))
6133             return false;
6134         key++; pos++;
6135         }
6136     return true;
6141 /**
6142  *  Add an include file name to a file record.  If the name
6143  *  is not found in allFiles explicitly, try prepending include
6144  *  directory names to it and try again.
6145  */
6146 bool DepTool::addIncludeFile(FileRec *frec, const String &iname)
6148     //# if the name is an exact match to a path name
6149     //# in allFiles, like "myinc.h"
6150     std::map<String, FileRec *>::iterator iter =
6151            allFiles.find(iname);
6152     if (iter != allFiles.end()) //already exists
6153         {
6154          //h file in same dir
6155         FileRec *other = iter->second;
6156         //trace("local: '%s'", iname.c_str());
6157         frec->files[iname] = other;
6158         return true;
6159         }
6160     else 
6161         {
6162         //## Ok, it was not found directly
6163         //look in other dirs
6164         std::vector<String>::iterator diter;
6165         for (diter=directories.begin() ;
6166              diter!=directories.end() ; diter++)
6167             {
6168             String dfname = *diter;
6169             dfname.append("/");
6170             dfname.append(iname);
6171             URI fullPathURI(dfname);  //normalize path name
6172             String fullPath = fullPathURI.getPath();
6173             if (fullPath[0] == '/')
6174                 fullPath = fullPath.substr(1);
6175             //trace("Normalized %s to %s", dfname.c_str(), fullPath.c_str());
6176             iter = allFiles.find(fullPath);
6177             if (iter != allFiles.end())
6178                 {
6179                 FileRec *other = iter->second;
6180                 //trace("other: '%s'", iname.c_str());
6181                 frec->files[fullPath] = other;
6182                 return true;
6183                 }
6184             }
6185         }
6186     return true;
6191 /**
6192  *  Lightly parse a file to find the #include directives.  Do
6193  *  a bit of state machine stuff to make sure that the directive
6194  *  is valid.  (Like not in a comment).
6195  */
6196 bool DepTool::scanFile(const String &fname, FileRec *frec)
6198     String fileName;
6199     if (sourceDir.size() > 0)
6200         {
6201         fileName.append(sourceDir);
6202         fileName.append("/");
6203         }
6204     fileName.append(fname);
6205     String nativeName = getNativePath(fileName);
6206     FILE *f = fopen(nativeName.c_str(), "r");
6207     if (!f)
6208         {
6209         error("Could not open '%s' for reading", fname.c_str());
6210         return false;
6211         }
6212     String buf;
6213     while (!feof(f))
6214         {
6215         int nrbytes = fread(readBuf, 1, readBufSize, f);
6216         readBuf[nrbytes] = '\0';
6217         buf.append(readBuf);
6218         }
6219     fclose(f);
6221     depFileSize = buf.size();
6222     depFileBuf  = (char *)buf.c_str();
6223     int pos = 0;
6226     while (pos < depFileSize)
6227         {
6228         //trace("p:%c", get(pos));
6230         //# Block comment
6231         if (get(pos) == '/' && get(pos+1) == '*')
6232             {
6233             pos += 2;
6234             while (pos < depFileSize)
6235                 {
6236                 if (get(pos) == '*' && get(pos+1) == '/')
6237                     {
6238                     pos += 2;
6239                     break;
6240                     }
6241                 else
6242                     pos++;
6243                 }
6244             }
6245         //# Line comment
6246         else if (get(pos) == '/' && get(pos+1) == '/')
6247             {
6248             pos += 2;
6249             while (pos < depFileSize)
6250                 {
6251                 if (get(pos) == '\n')
6252                     {
6253                     pos++;
6254                     break;
6255                     }
6256                 else
6257                     pos++;
6258                 }
6259             }
6260         //# #include! yaay
6261         else if (sequ(pos, "#include"))
6262             {
6263             pos += 8;
6264             pos = skipwhite(pos);
6265             String iname;
6266             pos = getword(pos, iname);
6267             if (iname.size()>2)
6268                 {
6269                 iname = iname.substr(1, iname.size()-2);
6270                 addIncludeFile(frec, iname);
6271                 }
6272             }
6273         else
6274             {
6275             pos++;
6276             }
6277         }
6279     return true;
6284 /**
6285  *  Recursively check include lists to find all files in allFiles to which
6286  *  a given file is dependent.
6287  */
6288 bool DepTool::processDependency(FileRec *ofile, FileRec *include)
6290     std::map<String, FileRec *>::iterator iter;
6291     for (iter=include->files.begin() ; iter!=include->files.end() ; iter++)
6292         {
6293         String fname  = iter->first;
6294         if (ofile->files.find(fname) != ofile->files.end())
6295             {
6296             //trace("file '%s' already seen", fname.c_str());
6297             continue;
6298             }
6299         FileRec *child  = iter->second;
6300         ofile->files[fname] = child;
6301       
6302         processDependency(ofile, child);
6303         }
6306     return true;
6313 /**
6314  *  Generate the file dependency list.
6315  */
6316 bool DepTool::generateDependencies()
6318     std::map<String, FileRec *>::iterator iter;
6319     //# First pass.  Scan for all includes
6320     for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
6321         {
6322         FileRec *frec = iter->second;
6323         if (!scanFile(iter->first, frec))
6324             {
6325             //quit?
6326             }
6327         }
6329     //# Second pass.  Scan for all includes
6330     for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
6331         {
6332         FileRec *include = iter->second;
6333         if (include->type == FileRec::CFILE)
6334             {
6335             //String cFileName   = iter->first;
6336             FileRec *ofile     = new FileRec(FileRec::OFILE);
6337             ofile->path        = include->path;
6338             ofile->baseName    = include->baseName;
6339             ofile->suffix      = include->suffix;
6340             String fname       = include->path;
6341             if (fname.size()>0)
6342                 fname.append("/");
6343             fname.append(include->baseName);
6344             fname.append(".o");
6345             oFiles[fname]    = ofile;
6346             //add the .c file first?   no, don't
6347             //ofile->files[cFileName] = include;
6348             
6349             //trace("ofile:%s", fname.c_str());
6351             processDependency(ofile, include);
6352             }
6353         }
6355       
6356     return true;
6361 /**
6362  *  High-level call to generate deps and optionally save them
6363  */
6364 bool DepTool::generateDependencies(const String &fileName)
6366     if (!createFileList())
6367         return false;
6368     if (!generateDependencies())
6369         return false;
6370     if (!saveDepFile(fileName))
6371         return false;
6372     return true;
6376 /**
6377  *   This saves the dependency cache.
6378  */
6379 bool DepTool::saveDepFile(const String &fileName)
6381     time_t tim;
6382     time(&tim);
6384     FILE *f = fopen(fileName.c_str(), "w");
6385     if (!f)
6386         {
6387         trace("cannot open '%s' for writing", fileName.c_str());
6388         }
6389     fprintf(f, "<?xml version='1.0'?>\n");
6390     fprintf(f, "<!--\n");
6391     fprintf(f, "########################################################\n");
6392     fprintf(f, "## File: build.dep\n");
6393     fprintf(f, "## Generated by BuildTool at :%s", ctime(&tim));
6394     fprintf(f, "########################################################\n");
6395     fprintf(f, "-->\n");
6397     fprintf(f, "<dependencies source='%s'>\n\n", sourceDir.c_str());
6398     std::map<String, FileRec *>::iterator iter;
6399     for (iter=oFiles.begin() ; iter!=oFiles.end() ; iter++)
6400         {
6401         FileRec *frec = iter->second;
6402         if (frec->type == FileRec::OFILE)
6403             {
6404             fprintf(f, "<object path='%s' name='%s' suffix='%s'>\n",
6405                  frec->path.c_str(), frec->baseName.c_str(), frec->suffix.c_str());
6406             std::map<String, FileRec *>::iterator citer;
6407             for (citer=frec->files.begin() ; citer!=frec->files.end() ; citer++)
6408                 {
6409                 String cfname = citer->first;
6410                 fprintf(f, "    <dep name='%s'/>\n", cfname.c_str());
6411                 }
6412             fprintf(f, "</object>\n\n");
6413             }
6414         }
6416     fprintf(f, "</dependencies>\n");
6417     fprintf(f, "\n");
6418     fprintf(f, "<!--\n");
6419     fprintf(f, "########################################################\n");
6420     fprintf(f, "## E N D\n");
6421     fprintf(f, "########################################################\n");
6422     fprintf(f, "-->\n");
6424     fclose(f);
6426     return true;
6432 /**
6433  *   This loads the dependency cache.
6434  */
6435 std::vector<DepRec> DepTool::loadDepFile(const String &depFile)
6437     std::vector<DepRec> result;
6438     
6439     Parser parser;
6440     Element *root = parser.parseFile(depFile.c_str());
6441     if (!root)
6442         {
6443         //error("Could not open %s for reading", depFile.c_str());
6444         return result;
6445         }
6447     if (root->getChildren().size()==0 ||
6448         root->getChildren()[0]->getName()!="dependencies")
6449         {
6450         error("loadDepFile: main xml element should be <dependencies>");
6451         delete root;
6452         return result;
6453         }
6455     //########## Start parsing
6456     Element *depList = root->getChildren()[0];
6458     std::vector<Element *> objects = depList->getChildren();
6459     for (unsigned int i=0 ; i<objects.size() ; i++)
6460         {
6461         Element *objectElem = objects[i];
6462         String tagName = objectElem->getName();
6463         if (tagName != "object")
6464             {
6465             error("loadDepFile: <dependencies> should have only <object> children");
6466             return result;
6467             }
6469         String objName   = objectElem->getAttribute("name");
6470          //trace("object:%s", objName.c_str());
6471         DepRec depObject(objName);
6472         depObject.path   = objectElem->getAttribute("path");
6473         depObject.suffix = objectElem->getAttribute("suffix");
6474         //########## DESCRIPTION
6475         std::vector<Element *> depElems = objectElem->getChildren();
6476         for (unsigned int i=0 ; i<depElems.size() ; i++)
6477             {
6478             Element *depElem = depElems[i];
6479             tagName = depElem->getName();
6480             if (tagName != "dep")
6481                 {
6482                 error("loadDepFile: <object> should have only <dep> children");
6483                 return result;
6484                 }
6485             String depName = depElem->getAttribute("name");
6486             //trace("    dep:%s", depName.c_str());
6487             depObject.files.push_back(depName);
6488             }
6490         //Insert into the result list, in a sorted manner
6491         bool inserted = false;
6492         std::vector<DepRec>::iterator iter;
6493         for (iter = result.begin() ; iter != result.end() ; iter++)
6494             {
6495             String vpath = iter->path;
6496             vpath.append("/");
6497             vpath.append(iter->name);
6498             String opath = depObject.path;
6499             opath.append("/");
6500             opath.append(depObject.name);
6501             if (vpath > opath)
6502                 {
6503                 inserted = true;
6504                 iter = result.insert(iter, depObject);
6505                 break;
6506                 }
6507             }
6508         if (!inserted)
6509             result.push_back(depObject);
6510         }
6512     delete root;
6514     return result;
6518 /**
6519  *   This loads the dependency cache.
6520  */
6521 std::vector<DepRec> DepTool::getDepFile(const String &depFile,
6522                    bool forceRefresh)
6524     std::vector<DepRec> result;
6525     if (forceRefresh)
6526         {
6527         generateDependencies(depFile);
6528         result = loadDepFile(depFile);
6529         }
6530     else
6531         {
6532         //try once
6533         result = loadDepFile(depFile);
6534         if (result.size() == 0)
6535             {
6536             //fail? try again
6537             generateDependencies(depFile);
6538             result = loadDepFile(depFile);
6539             }
6540         }
6541     return result;
6547 //########################################################################
6548 //# T A S K
6549 //########################################################################
6550 //forward decl
6551 class Target;
6552 class Make;
6554 /**
6555  *
6556  */
6557 class Task : public MakeBase
6560 public:
6562     typedef enum
6563         {
6564         TASK_NONE,
6565         TASK_CC,
6566         TASK_COPY,
6567         TASK_CXXTEST_PART,
6568         TASK_CXXTEST_ROOT,
6569         TASK_CXXTEST_RUN,
6570         TASK_DELETE,
6571         TASK_ECHO,
6572         TASK_JAR,
6573         TASK_JAVAC,
6574         TASK_LINK,
6575         TASK_MAKEFILE,
6576         TASK_MKDIR,
6577         TASK_MSGFMT,
6578         TASK_PKG_CONFIG,
6579         TASK_RANLIB,
6580         TASK_RC,
6581         TASK_SHAREDLIB,
6582         TASK_STATICLIB,
6583         TASK_STRIP,
6584         TASK_TOUCH,
6585         TASK_TSTAMP
6586         } TaskType;
6587         
6589     /**
6590      *
6591      */
6592     Task(MakeBase &par) : parent(par)
6593         { init(); }
6595     /**
6596      *
6597      */
6598     Task(const Task &other) : parent(other.parent)
6599         { init(); assign(other); }
6601     /**
6602      *
6603      */
6604     Task &operator=(const Task &other)
6605         { assign(other); return *this; }
6607     /**
6608      *
6609      */
6610     virtual ~Task()
6611         { }
6614     /**
6615      *
6616      */
6617     virtual MakeBase &getParent()
6618         { return parent; }
6620      /**
6621      *
6622      */
6623     virtual int  getType()
6624         { return type; }
6626     /**
6627      *
6628      */
6629     virtual void setType(int val)
6630         { type = val; }
6632     /**
6633      *
6634      */
6635     virtual String getName()
6636         { return name; }
6638     /**
6639      *
6640      */
6641     virtual bool execute()
6642         { return true; }
6644     /**
6645      *
6646      */
6647     virtual bool parse(Element *elem)
6648         { return true; }
6650     /**
6651      *
6652      */
6653     Task *createTask(Element *elem, int lineNr);
6656 protected:
6658     void init()
6659         {
6660         type = TASK_NONE;
6661         name = "none";
6662         }
6664     void assign(const Task &other)
6665         {
6666         type = other.type;
6667         name = other.name;
6668         }
6669         
6670     /**
6671      *  Show task status
6672      */
6673     void taskstatus(const char *fmt, ...)
6674         {
6675         va_list args;
6676         va_start(args,fmt);
6677         fprintf(stdout, "    %s : ", name.c_str());
6678         vfprintf(stdout, fmt, args);
6679         fprintf(stdout, "\n");
6680         va_end(args) ;
6681         }
6683     String getAttribute(Element *elem, const String &attrName)
6684         {
6685         String str;
6686         return str;
6687         }
6689     MakeBase &parent;
6691     int type;
6693     String name;
6694 };
6698 /**
6699  * This task runs the C/C++ compiler.  The compiler is invoked
6700  * for all .c or .cpp files which are newer than their correcsponding
6701  * .o files.  
6702  */
6703 class TaskCC : public Task
6705 public:
6707     TaskCC(MakeBase &par) : Task(par)
6708         {
6709         type = TASK_CC;
6710         name = "cc";
6711         }
6713     virtual ~TaskCC()
6714         {}
6715         
6716     virtual bool isExcludedInc(const String &dirname)
6717         {
6718         for (unsigned int i=0 ; i<excludeInc.size() ; i++)
6719             {
6720             String fname = excludeInc[i];
6721             if (fname == dirname)
6722                 return true;
6723             }
6724         return false;
6725         }
6727     virtual bool execute()
6728         {
6729         //evaluate our parameters
6730         String command         = parent.eval(commandOpt, "gcc");
6731         String ccCommand       = parent.eval(ccCommandOpt, "gcc");
6732         String cxxCommand      = parent.eval(cxxCommandOpt, "g++");
6733         String source          = parent.eval(sourceOpt, ".");
6734         String dest            = parent.eval(destOpt, ".");
6735         String flags           = parent.eval(flagsOpt, "");
6736         String defines         = parent.eval(definesOpt, "");
6737         String includes        = parent.eval(includesOpt, "");
6738         bool continueOnError   = parent.evalBool(continueOnErrorOpt, true);
6739         bool refreshCache      = parent.evalBool(refreshCacheOpt, false);
6741         if (!listFiles(parent, fileSet))
6742             return false;
6743             
6744         FILE *f = NULL;
6745         f = fopen("compile.lst", "w");
6747         //refreshCache is probably false here, unless specified otherwise
6748         String fullName = parent.resolve("build.dep");
6749         if (refreshCache || isNewerThan(parent.getURI().getPath(), fullName))
6750             {
6751             taskstatus("regenerating C/C++ dependency cache");
6752             refreshCache = true;
6753             }
6755         DepTool depTool;
6756         depTool.setSourceDirectory(source);
6757         depTool.setFileList(fileSet.getFiles());
6758         std::vector<DepRec> deps =
6759              depTool.getDepFile("build.dep", refreshCache);
6760         
6761         String incs;
6762         incs.append("-I");
6763         incs.append(parent.resolve("."));
6764         incs.append(" ");
6765         if (includes.size()>0)
6766             {
6767             incs.append(includes);
6768             incs.append(" ");
6769             }
6770         std::set<String> paths;
6771         std::vector<DepRec>::iterator viter;
6772         for (viter=deps.begin() ; viter!=deps.end() ; viter++)
6773             {
6774             DepRec dep = *viter;
6775             if (dep.path.size()>0)
6776                 paths.insert(dep.path);
6777             }
6778         if (source.size()>0)
6779             {
6780             incs.append(" -I");
6781             incs.append(parent.resolve(source));
6782             incs.append(" ");
6783             }
6784         std::set<String>::iterator setIter;
6785         for (setIter=paths.begin() ; setIter!=paths.end() ; setIter++)
6786             {
6787             String dirName = *setIter;
6788             //check excludeInc to see if we dont want to include this dir
6789             if (isExcludedInc(dirName))
6790                 continue;
6791             incs.append(" -I");
6792             String dname;
6793             if (source.size()>0)
6794                 {
6795                 dname.append(source);
6796                 dname.append("/");
6797                 }
6798             dname.append(dirName);
6799             incs.append(parent.resolve(dname));
6800             }
6801             
6802         /**
6803          * Compile each of the C files that need it
6804          */
6805         bool errorOccurred = false;                 
6806         std::vector<String> cfiles;
6807         for (viter=deps.begin() ; viter!=deps.end() ; viter++)
6808             {
6809             DepRec dep = *viter;
6811             //## Select command
6812             String sfx = dep.suffix;
6813             String command = ccCommand;
6814             if (sfx == "cpp" || sfx == "cxx" || sfx == "c++" ||
6815                  sfx == "cc" || sfx == "CC")
6816                 command = cxxCommand;
6817  
6818             //## Make paths
6819             String destPath = dest;
6820             String srcPath  = source;
6821             if (dep.path.size()>0)
6822                 {
6823                 destPath.append("/");
6824                 destPath.append(dep.path);
6825                 srcPath.append("/");
6826                 srcPath.append(dep.path);
6827                 }
6828             //## Make sure destination directory exists
6829             if (!createDirectory(destPath))
6830                 return false;
6831                 
6832             //## Check whether it needs to be done
6833             String destName;
6834             if (destPath.size()>0)
6835                 {
6836                 destName.append(destPath);
6837                 destName.append("/");
6838                 }
6839             destName.append(dep.name);
6840             destName.append(".o");
6841             String destFullName = parent.resolve(destName);
6842             String srcName;
6843             if (srcPath.size()>0)
6844                 {
6845                 srcName.append(srcPath);
6846                 srcName.append("/");
6847                 }
6848             srcName.append(dep.name);
6849             srcName.append(".");
6850             srcName.append(dep.suffix);
6851             String srcFullName = parent.resolve(srcName);
6852             bool compileMe = false;
6853             //# First we check if the source is newer than the .o
6854             if (isNewerThan(srcFullName, destFullName))
6855                 {
6856                 taskstatus("compile of %s required by source: %s",
6857                         destFullName.c_str(), srcFullName.c_str());
6858                 compileMe = true;
6859                 }
6860             else
6861                 {
6862                 //# secondly, we check if any of the included dependencies
6863                 //# of the .c/.cpp is newer than the .o
6864                 for (unsigned int i=0 ; i<dep.files.size() ; i++)
6865                     {
6866                     String depName;
6867                     if (source.size()>0)
6868                         {
6869                         depName.append(source);
6870                         depName.append("/");
6871                         }
6872                     depName.append(dep.files[i]);
6873                     String depFullName = parent.resolve(depName);
6874                     bool depRequires = isNewerThan(depFullName, destFullName);
6875                     //trace("%d %s %s\n", depRequires,
6876                     //        destFullName.c_str(), depFullName.c_str());
6877                     if (depRequires)
6878                         {
6879                         taskstatus("compile of %s required by included: %s",
6880                                 destFullName.c_str(), depFullName.c_str());
6881                         compileMe = true;
6882                         break;
6883                         }
6884                     }
6885                 }
6886             if (!compileMe)
6887                 {
6888                 continue;
6889                 }
6891             //## Assemble the command
6892             String cmd = command;
6893             cmd.append(" -c ");
6894             cmd.append(flags);
6895             cmd.append(" ");
6896             cmd.append(defines);
6897             cmd.append(" ");
6898             cmd.append(incs);
6899             cmd.append(" ");
6900             cmd.append(srcFullName);
6901             cmd.append(" -o ");
6902             cmd.append(destFullName);
6904             //## Execute the command
6906             String outString, errString;
6907             bool ret = executeCommand(cmd.c_str(), "", outString, errString);
6909             if (f)
6910                 {
6911                 fprintf(f, "########################### File : %s\n",
6912                              srcFullName.c_str());
6913                 fprintf(f, "#### COMMAND ###\n");
6914                 int col = 0;
6915                 for (unsigned int i = 0 ; i < cmd.size() ; i++)
6916                     {
6917                     char ch = cmd[i];
6918                     if (isspace(ch)  && col > 63)
6919                         {
6920                         fputc('\n', f);
6921                         col = 0;
6922                         }
6923                     else
6924                         {
6925                         fputc(ch, f);
6926                         col++;
6927                         }
6928                     if (col > 76)
6929                         {
6930                         fputc('\n', f);
6931                         col = 0;
6932                         }
6933                     }
6934                 fprintf(f, "\n");
6935                 fprintf(f, "#### STDOUT ###\n%s\n", outString.c_str());
6936                 fprintf(f, "#### STDERR ###\n%s\n\n", errString.c_str());
6937                 fflush(f);
6938                 }
6939             if (!ret)
6940                 {
6941                 error("problem compiling: %s", errString.c_str());
6942                 errorOccurred = true;
6943                 }
6944             if (errorOccurred && !continueOnError)
6945                 break;
6947             removeFromStatCache(getNativePath(destFullName));
6948             }
6950         if (f)
6951             {
6952             fclose(f);
6953             }
6954         
6955         return !errorOccurred;
6956         }
6959     virtual bool parse(Element *elem)
6960         {
6961         String s;
6962         if (!parent.getAttribute(elem, "command", commandOpt))
6963             return false;
6964         if (commandOpt.size()>0)
6965             { cxxCommandOpt = ccCommandOpt = commandOpt; }
6966         if (!parent.getAttribute(elem, "cc", ccCommandOpt))
6967             return false;
6968         if (!parent.getAttribute(elem, "cxx", cxxCommandOpt))
6969             return false;
6970         if (!parent.getAttribute(elem, "destdir", destOpt))
6971             return false;
6972         if (!parent.getAttribute(elem, "continueOnError", continueOnErrorOpt))
6973             return false;
6974         if (!parent.getAttribute(elem, "refreshCache", refreshCacheOpt))
6975             return false;
6977         std::vector<Element *> children = elem->getChildren();
6978         for (unsigned int i=0 ; i<children.size() ; i++)
6979             {
6980             Element *child = children[i];
6981             String tagName = child->getName();
6982             if (tagName == "flags")
6983                 {
6984                 if (!parent.getValue(child, flagsOpt))
6985                     return false;
6986                 flagsOpt = strip(flagsOpt);
6987                 }
6988             else if (tagName == "includes")
6989                 {
6990                 if (!parent.getValue(child, includesOpt))
6991                     return false;
6992                 includesOpt = strip(includesOpt);
6993                 }
6994             else if (tagName == "defines")
6995                 {
6996                 if (!parent.getValue(child, definesOpt))
6997                     return false;
6998                 definesOpt = strip(definesOpt);
6999                 }
7000             else if (tagName == "fileset")
7001                 {
7002                 if (!parseFileSet(child, parent, fileSet))
7003                     return false;
7004                 sourceOpt = fileSet.getDirectory();
7005                 }
7006             else if (tagName == "excludeinc")
7007                 {
7008                 if (!parseFileList(child, parent, excludeInc))
7009                     return false;
7010                 }
7011             }
7013         return true;
7014         }
7015         
7016 protected:
7018     String   commandOpt;
7019     String   ccCommandOpt;
7020     String   cxxCommandOpt;
7021     String   sourceOpt;
7022     String   destOpt;
7023     String   flagsOpt;
7024     String   definesOpt;
7025     String   includesOpt;
7026     String   continueOnErrorOpt;
7027     String   refreshCacheOpt;
7028     FileSet  fileSet;
7029     FileList excludeInc;
7030     
7031 };
7035 /**
7036  *
7037  */
7038 class TaskCopy : public Task
7040 public:
7042     typedef enum
7043         {
7044         CP_NONE,
7045         CP_TOFILE,
7046         CP_TODIR
7047         } CopyType;
7049     TaskCopy(MakeBase &par) : Task(par)
7050         {
7051         type        = TASK_COPY;
7052         name        = "copy";
7053         cptype      = CP_NONE;
7054         haveFileSet = false;
7055         }
7057     virtual ~TaskCopy()
7058         {}
7060     virtual bool execute()
7061         {
7062         String fileName   = parent.eval(fileNameOpt   , ".");
7063         String toFileName = parent.eval(toFileNameOpt , ".");
7064         String toDirName  = parent.eval(toDirNameOpt  , ".");
7065         bool   verbose    = parent.evalBool(verboseOpt, false);
7066         switch (cptype)
7067            {
7068            case CP_TOFILE:
7069                {
7070                if (fileName.size()>0)
7071                    {
7072                    taskstatus("%s to %s",
7073                         fileName.c_str(), toFileName.c_str());
7074                    String fullSource = parent.resolve(fileName);
7075                    String fullDest = parent.resolve(toFileName);
7076                    if (verbose)
7077                        taskstatus("copy %s to file %s", fullSource.c_str(),
7078                                           fullDest.c_str());
7079                    if (!isRegularFile(fullSource))
7080                        {
7081                        error("copy : file %s does not exist", fullSource.c_str());
7082                        return false;
7083                        }
7084                    if (!isNewerThan(fullSource, fullDest))
7085                        {
7086                        taskstatus("skipped");
7087                        return true;
7088                        }
7089                    if (!copyFile(fullSource, fullDest))
7090                        return false;
7091                    taskstatus("1 file copied");
7092                    }
7093                return true;
7094                }
7095            case CP_TODIR:
7096                {
7097                if (haveFileSet)
7098                    {
7099                    if (!listFiles(parent, fileSet))
7100                        return false;
7101                    String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
7103                    taskstatus("%s to %s",
7104                        fileSetDir.c_str(), toDirName.c_str());
7106                    int nrFiles = 0;
7107                    for (unsigned int i=0 ; i<fileSet.size() ; i++)
7108                        {
7109                        String fileName = fileSet[i];
7111                        String sourcePath;
7112                        if (fileSetDir.size()>0)
7113                            {
7114                            sourcePath.append(fileSetDir);
7115                            sourcePath.append("/");
7116                            }
7117                        sourcePath.append(fileName);
7118                        String fullSource = parent.resolve(sourcePath);
7119                        
7120                        //Get the immediate parent directory's base name
7121                        String baseFileSetDir = fileSetDir;
7122                        unsigned int pos = baseFileSetDir.find_last_of('/');
7123                        if (pos!=baseFileSetDir.npos &&
7124                                   pos < baseFileSetDir.size()-1)
7125                            baseFileSetDir =
7126                               baseFileSetDir.substr(pos+1,
7127                                    baseFileSetDir.size());
7128                        //Now make the new path
7129                        String destPath;
7130                        if (toDirName.size()>0)
7131                            {
7132                            destPath.append(toDirName);
7133                            destPath.append("/");
7134                            }
7135                        if (baseFileSetDir.size()>0)
7136                            {
7137                            destPath.append(baseFileSetDir);
7138                            destPath.append("/");
7139                            }
7140                        destPath.append(fileName);
7141                        String fullDest = parent.resolve(destPath);
7142                        //trace("fileName:%s", fileName.c_str());
7143                        if (verbose)
7144                            taskstatus("copy %s to new dir : %s",
7145                                  fullSource.c_str(), fullDest.c_str());
7146                        if (!isNewerThan(fullSource, fullDest))
7147                            {
7148                            if (verbose)
7149                                taskstatus("copy skipping %s", fullSource.c_str());
7150                            continue;
7151                            }
7152                        if (!copyFile(fullSource, fullDest))
7153                            return false;
7154                        nrFiles++;
7155                        }
7156                    taskstatus("%d file(s) copied", nrFiles);
7157                    }
7158                else //file source
7159                    {
7160                    //For file->dir we want only the basename of
7161                    //the source appended to the dest dir
7162                    taskstatus("%s to %s", 
7163                        fileName.c_str(), toDirName.c_str());
7164                    String baseName = fileName;
7165                    unsigned int pos = baseName.find_last_of('/');
7166                    if (pos!=baseName.npos && pos<baseName.size()-1)
7167                        baseName = baseName.substr(pos+1, baseName.size());
7168                    String fullSource = parent.resolve(fileName);
7169                    String destPath;
7170                    if (toDirName.size()>0)
7171                        {
7172                        destPath.append(toDirName);
7173                        destPath.append("/");
7174                        }
7175                    destPath.append(baseName);
7176                    String fullDest = parent.resolve(destPath);
7177                    if (verbose)
7178                        taskstatus("file %s to new dir : %s", fullSource.c_str(),
7179                                           fullDest.c_str());
7180                    if (!isRegularFile(fullSource))
7181                        {
7182                        error("copy : file %s does not exist", fullSource.c_str());
7183                        return false;
7184                        }
7185                    if (!isNewerThan(fullSource, fullDest))
7186                        {
7187                        taskstatus("skipped");
7188                        return true;
7189                        }
7190                    if (!copyFile(fullSource, fullDest))
7191                        return false;
7192                    taskstatus("1 file copied");
7193                    }
7194                return true;
7195                }
7196            }
7197         return true;
7198         }
7201     virtual bool parse(Element *elem)
7202         {
7203         if (!parent.getAttribute(elem, "file", fileNameOpt))
7204             return false;
7205         if (!parent.getAttribute(elem, "tofile", toFileNameOpt))
7206             return false;
7207         if (toFileNameOpt.size() > 0)
7208             cptype = CP_TOFILE;
7209         if (!parent.getAttribute(elem, "todir", toDirNameOpt))
7210             return false;
7211         if (toDirNameOpt.size() > 0)
7212             cptype = CP_TODIR;
7213         if (!parent.getAttribute(elem, "verbose", verboseOpt))
7214             return false;
7215             
7216         haveFileSet = false;
7217         
7218         std::vector<Element *> children = elem->getChildren();
7219         for (unsigned int i=0 ; i<children.size() ; i++)
7220             {
7221             Element *child = children[i];
7222             String tagName = child->getName();
7223             if (tagName == "fileset")
7224                 {
7225                 if (!parseFileSet(child, parent, fileSet))
7226                     {
7227                     error("problem getting fileset");
7228                     return false;
7229                     }
7230                 haveFileSet = true;
7231                 }
7232             }
7234         //Perform validity checks
7235         if (fileNameOpt.size()>0 && fileSet.size()>0)
7236             {
7237             error("<copy> can only have one of : file= and <fileset>");
7238             return false;
7239             }
7240         if (toFileNameOpt.size()>0 && toDirNameOpt.size()>0)
7241             {
7242             error("<copy> can only have one of : tofile= or todir=");
7243             return false;
7244             }
7245         if (haveFileSet && toDirNameOpt.size()==0)
7246             {
7247             error("a <copy> task with a <fileset> must have : todir=");
7248             return false;
7249             }
7250         if (cptype == CP_TOFILE && fileNameOpt.size()==0)
7251             {
7252             error("<copy> tofile= must be associated with : file=");
7253             return false;
7254             }
7255         if (cptype == CP_TODIR && fileNameOpt.size()==0 && !haveFileSet)
7256             {
7257             error("<copy> todir= must be associated with : file= or <fileset>");
7258             return false;
7259             }
7261         return true;
7262         }
7263         
7264 private:
7266     int cptype;
7267     bool haveFileSet;
7269     FileSet fileSet;
7270     String  fileNameOpt;
7271     String  toFileNameOpt;
7272     String  toDirNameOpt;
7273     String  verboseOpt;
7274 };
7277 /**
7278  * Generate CxxTest files
7279  */
7280 class TaskCxxTestPart: public Task
7282 public:
7284     TaskCxxTestPart(MakeBase &par) : Task(par)
7285          {
7286          type    = TASK_CXXTEST_PART;
7287          name    = "cxxtestpart";
7288          }
7290     virtual ~TaskCxxTestPart()
7291         {}
7293     virtual bool execute()
7294         {
7295         if (!listFiles(parent, fileSet))
7296             return false;
7297         String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
7298                 
7299         String fullDest = parent.resolve(parent.eval(destPathOpt, "."));
7300         String cmd = parent.eval(commandOpt, "cxxtestgen.py");
7301         cmd.append(" --part -o ");
7302         cmd.append(fullDest);
7304         unsigned int newFiles = 0;
7305         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7306             {
7307             String fileName = fileSet[i];
7308             if (getSuffix(fileName) != "h")
7309                 continue;
7310             String sourcePath;
7311             if (fileSetDir.size()>0)
7312                 {
7313                 sourcePath.append(fileSetDir);
7314                 sourcePath.append("/");
7315                 }
7316             sourcePath.append(fileName);
7317             String fullSource = parent.resolve(sourcePath);
7319             cmd.append(" ");
7320             cmd.append(fullSource);
7321             if (isNewerThan(fullSource, fullDest)) newFiles++;
7322             }
7323         
7324         if (newFiles>0) {
7325             size_t const lastSlash = fullDest.find_last_of('/');
7326             if (lastSlash != fullDest.npos) {
7327                 String directory(fullDest, 0, lastSlash);
7328                 if (!createDirectory(directory))
7329                     return false;
7330             }
7332             String outString, errString;
7333             if (!executeCommand(cmd.c_str(), "", outString, errString))
7334                 {
7335                 error("<cxxtestpart> problem: %s", errString.c_str());
7336                 return false;
7337                 }
7338             removeFromStatCache(getNativePath(fullDest));
7339         }
7341         return true;
7342         }
7344     virtual bool parse(Element *elem)
7345         {
7346         if (!parent.getAttribute(elem, "command", commandOpt))
7347             return false;
7348         if (!parent.getAttribute(elem, "out", destPathOpt))
7349             return false;
7350             
7351         std::vector<Element *> children = elem->getChildren();
7352         for (unsigned int i=0 ; i<children.size() ; i++)
7353             {
7354             Element *child = children[i];
7355             String tagName = child->getName();
7356             if (tagName == "fileset")
7357                 {
7358                 if (!parseFileSet(child, parent, fileSet))
7359                     return false;
7360                 }
7361             }
7362         return true;
7363         }
7365 private:
7367     String  commandOpt;
7368     String  destPathOpt;
7369     FileSet fileSet;
7371 };
7374 /**
7375  * Generate the CxxTest root file
7376  */
7377 class TaskCxxTestRoot: public Task
7379 public:
7381     TaskCxxTestRoot(MakeBase &par) : Task(par)
7382          {
7383          type    = TASK_CXXTEST_ROOT;
7384          name    = "cxxtestroot";
7385          }
7387     virtual ~TaskCxxTestRoot()
7388         {}
7390     virtual bool execute()
7391         {
7392         if (!listFiles(parent, fileSet))
7393             return false;
7394         String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
7395         unsigned int newFiles = 0;
7396                 
7397         String fullDest = parent.resolve(parent.eval(destPathOpt, "."));
7398         String cmd = parent.eval(commandOpt, "cxxtestgen.py");
7399         cmd.append(" --root -o ");
7400         cmd.append(fullDest);
7401         String templateFile = parent.eval(templateFileOpt, "");
7402         if (templateFile.size()>0) {
7403             String fullTemplate = parent.resolve(templateFile);
7404             cmd.append(" --template=");
7405             cmd.append(fullTemplate);
7406             if (isNewerThan(fullTemplate, fullDest)) newFiles++;
7407         }
7409         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7410             {
7411             String fileName = fileSet[i];
7412             if (getSuffix(fileName) != "h")
7413                 continue;
7414             String sourcePath;
7415             if (fileSetDir.size()>0)
7416                 {
7417                 sourcePath.append(fileSetDir);
7418                 sourcePath.append("/");
7419                 }
7420             sourcePath.append(fileName);
7421             String fullSource = parent.resolve(sourcePath);
7423             cmd.append(" ");
7424             cmd.append(fullSource);
7425             if (isNewerThan(fullSource, fullDest)) newFiles++;
7426             }
7427         
7428         if (newFiles>0) {
7429             size_t const lastSlash = fullDest.find_last_of('/');
7430             if (lastSlash != fullDest.npos) {
7431                 String directory(fullDest, 0, lastSlash);
7432                 if (!createDirectory(directory))
7433                     return false;
7434             }
7436             String outString, errString;
7437             if (!executeCommand(cmd.c_str(), "", outString, errString))
7438                 {
7439                 error("<cxxtestroot> problem: %s", errString.c_str());
7440                 return false;
7441                 }
7442             removeFromStatCache(getNativePath(fullDest));
7443         }
7445         return true;
7446         }
7448     virtual bool parse(Element *elem)
7449         {
7450         if (!parent.getAttribute(elem, "command", commandOpt))
7451             return false;
7452         if (!parent.getAttribute(elem, "template", templateFileOpt))
7453             return false;
7454         if (!parent.getAttribute(elem, "out", destPathOpt))
7455             return false;
7456             
7457         std::vector<Element *> children = elem->getChildren();
7458         for (unsigned int i=0 ; i<children.size() ; i++)
7459             {
7460             Element *child = children[i];
7461             String tagName = child->getName();
7462             if (tagName == "fileset")
7463                 {
7464                 if (!parseFileSet(child, parent, fileSet))
7465                     return false;
7466                 }
7467             }
7468         return true;
7469         }
7471 private:
7473     String  commandOpt;
7474     String  templateFileOpt;
7475     String  destPathOpt;
7476     FileSet fileSet;
7478 };
7481 /**
7482  * Execute the CxxTest test executable
7483  */
7484 class TaskCxxTestRun: public Task
7486 public:
7488     TaskCxxTestRun(MakeBase &par) : Task(par)
7489          {
7490          type    = TASK_CXXTEST_RUN;
7491          name    = "cxxtestrun";
7492          }
7494     virtual ~TaskCxxTestRun()
7495         {}
7497     virtual bool execute()
7498         {
7499         unsigned int newFiles = 0;
7500                 
7501         String workingDir = parent.resolve(parent.eval(workingDirOpt, "inkscape"));
7502         String rawCmd = parent.eval(commandOpt, "build/cxxtests");
7504         String cmdExe;
7505         if (fileExists(rawCmd)) {
7506             cmdExe = rawCmd;
7507         } else if (fileExists(rawCmd + ".exe")) {
7508             cmdExe = rawCmd + ".exe";
7509         } else {
7510             error("<cxxtestrun> problem: cxxtests executable not found! (command=\"%s\")", rawCmd.c_str());
7511         }
7512         // Note that the log file names are based on the exact name used to call cxxtests (it uses argv[0] + ".log"/".xml")
7513         if (isNewerThan(cmdExe, rawCmd + ".log") || isNewerThan(cmdExe, rawCmd + ".xml")) newFiles++;
7515         // Prepend the necessary ../'s
7516         String cmd = rawCmd;
7517         unsigned int workingDirDepth = 0;
7518         bool wasSlash = true;
7519         for(size_t i=0; i<workingDir.size(); i++) {
7520             // This assumes no . and .. parts
7521             if (wasSlash && workingDir[i]!='/') workingDirDepth++;
7522             wasSlash = workingDir[i] == '/';
7523         }
7524         for(size_t i=0; i<workingDirDepth; i++) {
7525             cmd = "../" + cmd;
7526         }
7527         
7528         if (newFiles>0) {
7529             char olddir[1024];
7530             if (workingDir.size()>0) {
7531                 // TODO: Double-check usage of getcwd and handle chdir errors
7532                 getcwd(olddir, 1024);
7533                 chdir(workingDir.c_str());
7534             }
7536             String outString;
7537             if (!executeCommand(cmd.c_str(), "", outString, outString))
7538                 {
7539                 error("<cxxtestrun> problem: %s", outString.c_str());
7540                 return false;
7541                 }
7543             if (workingDir.size()>0) {
7544                 // TODO: Handle errors?
7545                 chdir(olddir);
7546             }
7548             removeFromStatCache(getNativePath(cmd + ".log"));
7549             removeFromStatCache(getNativePath(cmd + ".xml"));
7550         }
7552         return true;
7553         }
7555     virtual bool parse(Element *elem)
7556         {
7557         if (!parent.getAttribute(elem, "command", commandOpt))
7558             return false;
7559         if (!parent.getAttribute(elem, "workingdir", workingDirOpt))
7560             return false;
7561         return true;
7562         }
7564 private:
7566     String  commandOpt;
7567     String  workingDirOpt;
7569 };
7572 /**
7573  *
7574  */
7575 class TaskDelete : public Task
7577 public:
7579     typedef enum
7580         {
7581         DEL_FILE,
7582         DEL_DIR,
7583         DEL_FILESET
7584         } DeleteType;
7586     TaskDelete(MakeBase &par) : Task(par)
7587         { 
7588         type        = TASK_DELETE;
7589         name        = "delete";
7590         delType     = DEL_FILE;
7591         }
7593     virtual ~TaskDelete()
7594         {}
7596     virtual bool execute()
7597         {
7598         String dirName   = parent.eval(dirNameOpt, ".");
7599         String fileName  = parent.eval(fileNameOpt, ".");
7600         bool verbose     = parent.evalBool(verboseOpt, false);
7601         bool quiet       = parent.evalBool(quietOpt, false);
7602         bool failOnError = parent.evalBool(failOnErrorOpt, true);
7603         switch (delType)
7604             {
7605             case DEL_FILE:
7606                 {
7607                 taskstatus("file: %s", fileName.c_str());
7608                 String fullName = parent.resolve(fileName);
7609                 char *fname = (char *)fullName.c_str();
7610                 if (!quiet && verbose)
7611                     taskstatus("path: %s", fname);
7612                 if (failOnError && !removeFile(fullName))
7613                     {
7614                     //error("Could not delete file '%s'", fullName.c_str());
7615                     return false;
7616                     }
7617                 return true;
7618                 }
7619             case DEL_DIR:
7620                 {
7621                 taskstatus("dir: %s", dirName.c_str());
7622                 String fullDir = parent.resolve(dirName);
7623                 if (!quiet && verbose)
7624                     taskstatus("path: %s", fullDir.c_str());
7625                 if (failOnError && !removeDirectory(fullDir))
7626                     {
7627                     //error("Could not delete directory '%s'", fullDir.c_str());
7628                     return false;
7629                     }
7630                 return true;
7631                 }
7632             }
7633         return true;
7634         }
7636     virtual bool parse(Element *elem)
7637         {
7638         if (!parent.getAttribute(elem, "file", fileNameOpt))
7639             return false;
7640         if (fileNameOpt.size() > 0)
7641             delType = DEL_FILE;
7642         if (!parent.getAttribute(elem, "dir", dirNameOpt))
7643             return false;
7644         if (dirNameOpt.size() > 0)
7645             delType = DEL_DIR;
7646         if (fileNameOpt.size()>0 && dirNameOpt.size()>0)
7647             {
7648             error("<delete> can have one attribute of file= or dir=");
7649             return false;
7650             }
7651         if (fileNameOpt.size()==0 && dirNameOpt.size()==0)
7652             {
7653             error("<delete> must have one attribute of file= or dir=");
7654             return false;
7655             }
7656         if (!parent.getAttribute(elem, "verbose", verboseOpt))
7657             return false;
7658         if (!parent.getAttribute(elem, "quiet", quietOpt))
7659             return false;
7660         if (!parent.getAttribute(elem, "failonerror", failOnErrorOpt))
7661             return false;
7662         return true;
7663         }
7665 private:
7667     int delType;
7668     String dirNameOpt;
7669     String fileNameOpt;
7670     String verboseOpt;
7671     String quietOpt;
7672     String failOnErrorOpt;
7673 };
7676 /**
7677  * Send a message to stdout
7678  */
7679 class TaskEcho : public Task
7681 public:
7683     TaskEcho(MakeBase &par) : Task(par)
7684         { type = TASK_ECHO; name = "echo"; }
7686     virtual ~TaskEcho()
7687         {}
7689     virtual bool execute()
7690         {
7691         //let message have priority over text
7692         String message = parent.eval(messageOpt, "");
7693         String text    = parent.eval(textOpt, "");
7694         if (message.size() > 0)
7695             {
7696             fprintf(stdout, "%s\n", message.c_str());
7697             }
7698         else if (text.size() > 0)
7699             {
7700             fprintf(stdout, "%s\n", text.c_str());
7701             }
7702         return true;
7703         }
7705     virtual bool parse(Element *elem)
7706         {
7707         if (!parent.getValue(elem, textOpt))
7708             return false;
7709         textOpt    = leftJustify(textOpt);
7710         if (!parent.getAttribute(elem, "message", messageOpt))
7711             return false;
7712         return true;
7713         }
7715 private:
7717     String messageOpt;
7718     String textOpt;
7719 };
7723 /**
7724  *
7725  */
7726 class TaskJar : public Task
7728 public:
7730     TaskJar(MakeBase &par) : Task(par)
7731         { type = TASK_JAR; name = "jar"; }
7733     virtual ~TaskJar()
7734         {}
7736     virtual bool execute()
7737         {
7738         String command  = parent.eval(commandOpt, "jar");
7739         String basedir  = parent.eval(basedirOpt, ".");
7740         String destfile = parent.eval(destfileOpt, ".");
7742         String cmd = command;
7743         cmd.append(" -cf ");
7744         cmd.append(destfile);
7745         cmd.append(" -C ");
7746         cmd.append(basedir);
7747         cmd.append(" .");
7749         String execCmd = cmd;
7751         String outString, errString;
7752         bool ret = executeCommand(execCmd.c_str(), "", outString, errString);
7753         if (!ret)
7754             {
7755             error("<jar> command '%s' failed :\n %s",
7756                                       execCmd.c_str(), errString.c_str());
7757             return false;
7758             }
7759         removeFromStatCache(getNativePath(destfile));
7760         return true;
7761         }
7763     virtual bool parse(Element *elem)
7764         {
7765         if (!parent.getAttribute(elem, "command", commandOpt))
7766             return false;
7767         if (!parent.getAttribute(elem, "basedir", basedirOpt))
7768             return false;
7769         if (!parent.getAttribute(elem, "destfile", destfileOpt))
7770             return false;
7771         if (basedirOpt.size() == 0 || destfileOpt.size() == 0)
7772             {
7773             error("<jar> required both basedir and destfile attributes to be set");
7774             return false;
7775             }
7776         return true;
7777         }
7779 private:
7781     String commandOpt;
7782     String basedirOpt;
7783     String destfileOpt;
7784 };
7787 /**
7788  *
7789  */
7790 class TaskJavac : public Task
7792 public:
7794     TaskJavac(MakeBase &par) : Task(par)
7795         { 
7796         type = TASK_JAVAC; name = "javac";
7797         }
7799     virtual ~TaskJavac()
7800         {}
7802     virtual bool execute()
7803         {
7804         String command  = parent.eval(commandOpt, "javac");
7805         String srcdir   = parent.eval(srcdirOpt, ".");
7806         String destdir  = parent.eval(destdirOpt, ".");
7807         String target   = parent.eval(targetOpt, "");
7809         std::vector<String> fileList;
7810         if (!listFiles(srcdir, "", fileList))
7811             {
7812             return false;
7813             }
7814         String cmd = command;
7815         cmd.append(" -d ");
7816         cmd.append(destdir);
7817         cmd.append(" -classpath ");
7818         cmd.append(destdir);
7819         cmd.append(" -sourcepath ");
7820         cmd.append(srcdir);
7821         cmd.append(" ");
7822         if (target.size()>0)
7823             {
7824             cmd.append(" -target ");
7825             cmd.append(target);
7826             cmd.append(" ");
7827             }
7828         String fname = "javalist.btool";
7829         FILE *f = fopen(fname.c_str(), "w");
7830         int count = 0;
7831         for (unsigned int i=0 ; i<fileList.size() ; i++)
7832             {
7833             String fname = fileList[i];
7834             String srcName = fname;
7835             if (fname.size()<6) //x.java
7836                 continue;
7837             if (fname.compare(fname.size()-5, 5, ".java") != 0)
7838                 continue;
7839             String baseName = fname.substr(0, fname.size()-5);
7840             String destName = baseName;
7841             destName.append(".class");
7843             String fullSrc = srcdir;
7844             fullSrc.append("/");
7845             fullSrc.append(fname);
7846             String fullDest = destdir;
7847             fullDest.append("/");
7848             fullDest.append(destName);
7849             //trace("fullsrc:%s fulldest:%s", fullSrc.c_str(), fullDest.c_str());
7850             if (!isNewerThan(fullSrc, fullDest))
7851                 continue;
7853             count++;
7854             fprintf(f, "%s\n", fullSrc.c_str());
7855             }
7856         fclose(f);
7857         if (!count)
7858             {
7859             taskstatus("nothing to do");
7860             return true;
7861             }
7863         taskstatus("compiling %d files", count);
7865         String execCmd = cmd;
7866         execCmd.append("@");
7867         execCmd.append(fname);
7869         String outString, errString;
7870         bool ret = executeCommand(execCmd.c_str(), "", outString, errString);
7871         if (!ret)
7872             {
7873             error("<javac> command '%s' failed :\n %s",
7874                                       execCmd.c_str(), errString.c_str());
7875             return false;
7876             }
7877         // TODO: 
7878         //removeFromStatCache(getNativePath(........));
7879         return true;
7880         }
7882     virtual bool parse(Element *elem)
7883         {
7884         if (!parent.getAttribute(elem, "command", commandOpt))
7885             return false;
7886         if (!parent.getAttribute(elem, "srcdir", srcdirOpt))
7887             return false;
7888         if (!parent.getAttribute(elem, "destdir", destdirOpt))
7889             return false;
7890         if (srcdirOpt.size() == 0 || destdirOpt.size() == 0)
7891             {
7892             error("<javac> required both srcdir and destdir attributes to be set");
7893             return false;
7894             }
7895         if (!parent.getAttribute(elem, "target", targetOpt))
7896             return false;
7897         return true;
7898         }
7900 private:
7902     String commandOpt;
7903     String srcdirOpt;
7904     String destdirOpt;
7905     String targetOpt;
7907 };
7910 /**
7911  *
7912  */
7913 class TaskLink : public Task
7915 public:
7917     TaskLink(MakeBase &par) : Task(par)
7918         {
7919         type = TASK_LINK; name = "link";
7920         }
7922     virtual ~TaskLink()
7923         {}
7925     virtual bool execute()
7926         {
7927         String  command        = parent.eval(commandOpt, "g++");
7928         String  fileName       = parent.eval(fileNameOpt, "");
7929         String  flags          = parent.eval(flagsOpt, "");
7930         String  libs           = parent.eval(libsOpt, "");
7931         bool    doStrip        = parent.evalBool(doStripOpt, false);
7932         String  symFileName    = parent.eval(symFileNameOpt, "");
7933         String  stripCommand   = parent.eval(stripCommandOpt, "strip");
7934         String  objcopyCommand = parent.eval(objcopyCommandOpt, "objcopy");
7936         if (!listFiles(parent, fileSet))
7937             return false;
7938         String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
7939         //trace("%d files in %s", fileSet.size(), fileSetDir.c_str());
7940         bool doit = false;
7941         String fullTarget = parent.resolve(fileName);
7942         String cmd = command;
7943         cmd.append(" -o ");
7944         cmd.append(fullTarget);
7945         cmd.append(" ");
7946         cmd.append(flags);
7947         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7948             {
7949             cmd.append(" ");
7950             String obj;
7951             if (fileSetDir.size()>0)
7952                 {
7953                 obj.append(fileSetDir);
7954                 obj.append("/");
7955                 }
7956             obj.append(fileSet[i]);
7957             String fullObj = parent.resolve(obj);
7958             String nativeFullObj = getNativePath(fullObj);
7959             cmd.append(nativeFullObj);
7960             //trace("link: tgt:%s obj:%s", fullTarget.c_str(),
7961             //          fullObj.c_str());
7962             if (isNewerThan(fullObj, fullTarget))
7963                 doit = true;
7964             }
7965         cmd.append(" ");
7966         cmd.append(libs);
7967         if (!doit)
7968             {
7969             //trace("link not needed");
7970             return true;
7971             }
7972         //trace("LINK cmd:%s", cmd.c_str());
7975         String outbuf, errbuf;
7976         if (!executeCommand(cmd.c_str(), "", outbuf, errbuf))
7977             {
7978             error("LINK problem: %s", errbuf.c_str());
7979             return false;
7980             }
7981         removeFromStatCache(getNativePath(fullTarget));
7983         if (symFileName.size()>0)
7984             {
7985             String symFullName = parent.resolve(symFileName);
7986             cmd = objcopyCommand;
7987             cmd.append(" --only-keep-debug ");
7988             cmd.append(getNativePath(fullTarget));
7989             cmd.append(" ");
7990             cmd.append(getNativePath(symFullName));
7991             if (!executeCommand(cmd, "", outbuf, errbuf))
7992                 {
7993                 error("<strip> symbol file failed : %s", errbuf.c_str());
7994                 return false;
7995                 }
7996             removeFromStatCache(getNativePath(symFullName));
7997             }
7998             
7999         if (doStrip)
8000             {
8001             cmd = stripCommand;
8002             cmd.append(" ");
8003             cmd.append(getNativePath(fullTarget));
8004             if (!executeCommand(cmd, "", outbuf, errbuf))
8005                {
8006                error("<strip> failed : %s", errbuf.c_str());
8007                return false;
8008                }
8009             removeFromStatCache(getNativePath(fullTarget));
8010             }
8012         return true;
8013         }
8015     virtual bool parse(Element *elem)
8016         {
8017         if (!parent.getAttribute(elem, "command", commandOpt))
8018             return false;
8019         if (!parent.getAttribute(elem, "objcopycommand", objcopyCommandOpt))
8020             return false;
8021         if (!parent.getAttribute(elem, "stripcommand", stripCommandOpt))
8022             return false;
8023         if (!parent.getAttribute(elem, "out", fileNameOpt))
8024             return false;
8025         if (!parent.getAttribute(elem, "strip", doStripOpt))
8026             return false;
8027         if (!parent.getAttribute(elem, "symfile", symFileNameOpt))
8028             return false;
8029             
8030         std::vector<Element *> children = elem->getChildren();
8031         for (unsigned int i=0 ; i<children.size() ; i++)
8032             {
8033             Element *child = children[i];
8034             String tagName = child->getName();
8035             if (tagName == "fileset")
8036                 {
8037                 if (!parseFileSet(child, parent, fileSet))
8038                     return false;
8039                 }
8040             else if (tagName == "flags")
8041                 {
8042                 if (!parent.getValue(child, flagsOpt))
8043                     return false;
8044                 flagsOpt = strip(flagsOpt);
8045                 }
8046             else if (tagName == "libs")
8047                 {
8048                 if (!parent.getValue(child, libsOpt))
8049                     return false;
8050                 libsOpt = strip(libsOpt);
8051                 }
8052             }
8053         return true;
8054         }
8056 private:
8058     FileSet fileSet;
8060     String  commandOpt;
8061     String  fileNameOpt;
8062     String  flagsOpt;
8063     String  libsOpt;
8064     String  doStripOpt;
8065     String  symFileNameOpt;
8066     String  stripCommandOpt;
8067     String  objcopyCommandOpt;
8069 };
8073 /**
8074  * Create a named file
8075  */
8076 class TaskMakeFile : public Task
8078 public:
8080     TaskMakeFile(MakeBase &par) : Task(par)
8081         { type = TASK_MAKEFILE; name = "makefile"; }
8083     virtual ~TaskMakeFile()
8084         {}
8086     virtual bool execute()
8087         {
8088         String fileName = parent.eval(fileNameOpt, "");
8089         String text     = parent.eval(textOpt, "");
8091         taskstatus("%s", fileName.c_str());
8092         String fullName = parent.resolve(fileName);
8093         if (!isNewerThan(parent.getURI().getPath(), fullName))
8094             {
8095             //trace("skipped <makefile>");
8096             return true;
8097             }
8098         String fullNative = getNativePath(fullName);
8099         //trace("fullName:%s", fullName.c_str());
8100         FILE *f = fopen(fullNative.c_str(), "w");
8101         if (!f)
8102             {
8103             error("<makefile> could not open %s for writing : %s",
8104                 fullName.c_str(), strerror(errno));
8105             return false;
8106             }
8107         for (unsigned int i=0 ; i<text.size() ; i++)
8108             fputc(text[i], f);
8109         fputc('\n', f);
8110         fclose(f);
8111         removeFromStatCache(fullNative);
8112         return true;
8113         }
8115     virtual bool parse(Element *elem)
8116         {
8117         if (!parent.getAttribute(elem, "file", fileNameOpt))
8118             return false;
8119         if (fileNameOpt.size() == 0)
8120             {
8121             error("<makefile> requires 'file=\"filename\"' attribute");
8122             return false;
8123             }
8124         if (!parent.getValue(elem, textOpt))
8125             return false;
8126         textOpt = leftJustify(textOpt);
8127         //trace("dirname:%s", dirName.c_str());
8128         return true;
8129         }
8131 private:
8133     String fileNameOpt;
8134     String textOpt;
8135 };
8139 /**
8140  * Create a named directory
8141  */
8142 class TaskMkDir : public Task
8144 public:
8146     TaskMkDir(MakeBase &par) : Task(par)
8147         { type = TASK_MKDIR; name = "mkdir"; }
8149     virtual ~TaskMkDir()
8150         {}
8152     virtual bool execute()
8153         {
8154         String dirName = parent.eval(dirNameOpt, ".");
8155         
8156         taskstatus("%s", dirName.c_str());
8157         String fullDir = parent.resolve(dirName);
8158         //trace("fullDir:%s", fullDir.c_str());
8159         if (!createDirectory(fullDir))
8160             return false;
8161         return true;
8162         }
8164     virtual bool parse(Element *elem)
8165         {
8166         if (!parent.getAttribute(elem, "dir", dirNameOpt))
8167             return false;
8168         if (dirNameOpt.size() == 0)
8169             {
8170             error("<mkdir> requires 'dir=\"dirname\"' attribute");
8171             return false;
8172             }
8173         return true;
8174         }
8176 private:
8178     String dirNameOpt;
8179 };
8183 /**
8184  * Create a named directory
8185  */
8186 class TaskMsgFmt: public Task
8188 public:
8190     TaskMsgFmt(MakeBase &par) : Task(par)
8191          { type = TASK_MSGFMT;  name = "msgfmt"; }
8193     virtual ~TaskMsgFmt()
8194         {}
8196     virtual bool execute()
8197         {
8198         String  command   = parent.eval(commandOpt, "msgfmt");
8199         String  toDirName = parent.eval(toDirNameOpt, ".");
8200         String  outName   = parent.eval(outNameOpt, "");
8201         bool    owndir    = parent.evalBool(owndirOpt, false);
8203         if (!listFiles(parent, fileSet))
8204             return false;
8205         String fileSetDir = fileSet.getDirectory();
8207         //trace("msgfmt: %d", fileSet.size());
8208         for (unsigned int i=0 ; i<fileSet.size() ; i++)
8209             {
8210             String fileName = fileSet[i];
8211             if (getSuffix(fileName) != "po")
8212                 continue;
8213             String sourcePath;
8214             if (fileSetDir.size()>0)
8215                 {
8216                 sourcePath.append(fileSetDir);
8217                 sourcePath.append("/");
8218                 }
8219             sourcePath.append(fileName);
8220             String fullSource = parent.resolve(sourcePath);
8222             String destPath;
8223             if (toDirName.size()>0)
8224                 {
8225                 destPath.append(toDirName);
8226                 destPath.append("/");
8227                 }
8228             if (owndir)
8229                 {
8230                 String subdir = fileName;
8231                 unsigned int pos = subdir.find_last_of('.');
8232                 if (pos != subdir.npos)
8233                     subdir = subdir.substr(0, pos);
8234                 destPath.append(subdir);
8235                 destPath.append("/");
8236                 }
8237             //Pick the output file name
8238             if (outName.size() > 0)
8239                 {
8240                 destPath.append(outName);
8241                 }
8242             else
8243                 {
8244                 destPath.append(fileName);
8245                 destPath[destPath.size()-2] = 'm';
8246                 }
8248             String fullDest = parent.resolve(destPath);
8250             if (!isNewerThan(fullSource, fullDest))
8251                 {
8252                 //trace("skip %s", fullSource.c_str());
8253                 continue;
8254                 }
8255                 
8256             String cmd = command;
8257             cmd.append(" ");
8258             cmd.append(fullSource);
8259             cmd.append(" -o ");
8260             cmd.append(fullDest);
8261             
8262             int pos = fullDest.find_last_of('/');
8263             if (pos>0)
8264                 {
8265                 String fullDestPath = fullDest.substr(0, pos);
8266                 if (!createDirectory(fullDestPath))
8267                     return false;
8268                 }
8272             String outString, errString;
8273             if (!executeCommand(cmd.c_str(), "", outString, errString))
8274                 {
8275                 error("<msgfmt> problem: %s", errString.c_str());
8276                 return false;
8277                 }
8278             removeFromStatCache(getNativePath(fullDest));
8279             }
8281         return true;
8282         }
8284     virtual bool parse(Element *elem)
8285         {
8286         if (!parent.getAttribute(elem, "command", commandOpt))
8287             return false;
8288         if (!parent.getAttribute(elem, "todir", toDirNameOpt))
8289             return false;
8290         if (!parent.getAttribute(elem, "out", outNameOpt))
8291             return false;
8292         if (!parent.getAttribute(elem, "owndir", owndirOpt))
8293             return false;
8294             
8295         std::vector<Element *> children = elem->getChildren();
8296         for (unsigned int i=0 ; i<children.size() ; i++)
8297             {
8298             Element *child = children[i];
8299             String tagName = child->getName();
8300             if (tagName == "fileset")
8301                 {
8302                 if (!parseFileSet(child, parent, fileSet))
8303                     return false;
8304                 }
8305             }
8306         return true;
8307         }
8309 private:
8311     FileSet fileSet;
8313     String  commandOpt;
8314     String  toDirNameOpt;
8315     String  outNameOpt;
8316     String  owndirOpt;
8318 };
8322 /**
8323  *  Perform a Package-Config query similar to pkg-config
8324  */
8325 class TaskPkgConfig : public Task
8327 public:
8329     typedef enum
8330         {
8331         PKG_CONFIG_QUERY_CFLAGS,
8332         PKG_CONFIG_QUERY_LIBS,
8333         PKG_CONFIG_QUERY_ALL
8334         } QueryTypes;
8336     TaskPkgConfig(MakeBase &par) : Task(par)
8337         {
8338         type = TASK_PKG_CONFIG;
8339         name = "pkg-config";
8340         }
8342     virtual ~TaskPkgConfig()
8343         {}
8345     virtual bool execute()
8346         {
8347         String pkgName       = parent.eval(pkgNameOpt,      "");
8348         String prefix        = parent.eval(prefixOpt,       "");
8349         String propName      = parent.eval(propNameOpt,     "");
8350         String pkgConfigPath = parent.eval(pkgConfigPathOpt,"");
8351         String query         = parent.eval(queryOpt,        "all");
8353         String path = parent.resolve(pkgConfigPath);
8354         PkgConfig pkgconfig;
8355         pkgconfig.setPath(path);
8356         pkgconfig.setPrefix(prefix);
8357         if (!pkgconfig.query(pkgName))
8358             {
8359             error("<pkg-config> query failed for '%s", name.c_str());
8360             return false;
8361             }
8362             
8363         String val = "";
8364         if (query == "cflags")
8365             val = pkgconfig.getCflags();
8366         else if (query == "libs")
8367             val =pkgconfig.getLibs();
8368         else if (query == "all")
8369             val = pkgconfig.getAll();
8370         else
8371             {
8372             error("<pkg-config> unhandled query : %s", query.c_str());
8373             return false;
8374             }
8375         taskstatus("property %s = '%s'", propName.c_str(), val.c_str());
8376         parent.setProperty(propName, val);
8377         return true;
8378         }
8380     virtual bool parse(Element *elem)
8381         {
8382         //# NAME
8383         if (!parent.getAttribute(elem, "name", pkgNameOpt))
8384             return false;
8385         if (pkgNameOpt.size()==0)
8386             {
8387             error("<pkg-config> requires 'name=\"package\"' attribute");
8388             return false;
8389             }
8391         //# PROPERTY
8392         if (!parent.getAttribute(elem, "property", propNameOpt))
8393             return false;
8394         if (propNameOpt.size()==0)
8395             {
8396             error("<pkg-config> requires 'property=\"name\"' attribute");
8397             return false;
8398             }
8399         //# PATH
8400         if (!parent.getAttribute(elem, "path", pkgConfigPathOpt))
8401             return false;
8402         //# PREFIX
8403         if (!parent.getAttribute(elem, "prefix", prefixOpt))
8404             return false;
8405         //# QUERY
8406         if (!parent.getAttribute(elem, "query", queryOpt))
8407             return false;
8409         return true;
8410         }
8412 private:
8414     String queryOpt;
8415     String pkgNameOpt;
8416     String prefixOpt;
8417     String propNameOpt;
8418     String pkgConfigPathOpt;
8420 };
8427 /**
8428  *  Process an archive to allow random access
8429  */
8430 class TaskRanlib : public Task
8432 public:
8434     TaskRanlib(MakeBase &par) : Task(par)
8435         { type = TASK_RANLIB; name = "ranlib"; }
8437     virtual ~TaskRanlib()
8438         {}
8440     virtual bool execute()
8441         {
8442         String fileName = parent.eval(fileNameOpt, "");
8443         String command  = parent.eval(commandOpt, "ranlib");
8445         String fullName = parent.resolve(fileName);
8446         //trace("fullDir:%s", fullDir.c_str());
8447         String cmd = command;
8448         cmd.append(" ");
8449         cmd.append(fullName);
8450         String outbuf, errbuf;
8451         if (!executeCommand(cmd, "", outbuf, errbuf))
8452             return false;
8453         // TODO:
8454         //removeFromStatCache(getNativePath(fullDest));
8455         return true;
8456         }
8458     virtual bool parse(Element *elem)
8459         {
8460         if (!parent.getAttribute(elem, "command", commandOpt))
8461             return false;
8462         if (!parent.getAttribute(elem, "file", fileNameOpt))
8463             return false;
8464         if (fileNameOpt.size() == 0)
8465             {
8466             error("<ranlib> requires 'file=\"fileNname\"' attribute");
8467             return false;
8468             }
8469         return true;
8470         }
8472 private:
8474     String fileNameOpt;
8475     String commandOpt;
8476 };
8480 /**
8481  * Compile a resource file into a binary object
8482  */
8483 class TaskRC : public Task
8485 public:
8487     TaskRC(MakeBase &par) : Task(par)
8488         { type = TASK_RC; name = "rc"; }
8490     virtual ~TaskRC()
8491         {}
8493     virtual bool execute()
8494         {
8495         String command  = parent.eval(commandOpt,  "windres");
8496         String flags    = parent.eval(flagsOpt,    "");
8497         String fileName = parent.eval(fileNameOpt, "");
8498         String outName  = parent.eval(outNameOpt,  "");
8500         String fullFile = parent.resolve(fileName);
8501         String fullOut  = parent.resolve(outName);
8502         if (!isNewerThan(fullFile, fullOut))
8503             return true;
8504         String cmd = command;
8505         cmd.append(" -o ");
8506         cmd.append(fullOut);
8507         cmd.append(" ");
8508         cmd.append(flags);
8509         cmd.append(" ");
8510         cmd.append(fullFile);
8512         String outString, errString;
8513         if (!executeCommand(cmd.c_str(), "", outString, errString))
8514             {
8515             error("RC problem: %s", errString.c_str());
8516             return false;
8517             }
8518         removeFromStatCache(getNativePath(fullOut));
8519         return true;
8520         }
8522     virtual bool parse(Element *elem)
8523         {
8524         if (!parent.getAttribute(elem, "command", commandOpt))
8525             return false;
8526         if (!parent.getAttribute(elem, "file", fileNameOpt))
8527             return false;
8528         if (!parent.getAttribute(elem, "out", outNameOpt))
8529             return false;
8530         std::vector<Element *> children = elem->getChildren();
8531         for (unsigned int i=0 ; i<children.size() ; i++)
8532             {
8533             Element *child = children[i];
8534             String tagName = child->getName();
8535             if (tagName == "flags")
8536                 {
8537                 if (!parent.getValue(child, flagsOpt))
8538                     return false;
8539                 }
8540             }
8541         return true;
8542         }
8544 private:
8546     String commandOpt;
8547     String flagsOpt;
8548     String fileNameOpt;
8549     String outNameOpt;
8551 };
8555 /**
8556  *  Collect .o's into a .so or DLL
8557  */
8558 class TaskSharedLib : public Task
8560 public:
8562     TaskSharedLib(MakeBase &par) : Task(par)
8563         { type = TASK_SHAREDLIB; name = "dll"; }
8565     virtual ~TaskSharedLib()
8566         {}
8568     virtual bool execute()
8569         {
8570         String command     = parent.eval(commandOpt, "dllwrap");
8571         String fileName    = parent.eval(fileNameOpt, "");
8572         String defFileName = parent.eval(defFileNameOpt, "");
8573         String impFileName = parent.eval(impFileNameOpt, "");
8574         String libs        = parent.eval(libsOpt, "");
8576         //trace("###########HERE %d", fileSet.size());
8577         bool doit = false;
8578         
8579         String fullOut = parent.resolve(fileName);
8580         //trace("ar fullout: %s", fullOut.c_str());
8581         
8582         if (!listFiles(parent, fileSet))
8583             return false;
8584         String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
8586         for (unsigned int i=0 ; i<fileSet.size() ; i++)
8587             {
8588             String fname;
8589             if (fileSetDir.size()>0)
8590                 {
8591                 fname.append(fileSetDir);
8592                 fname.append("/");
8593                 }
8594             fname.append(fileSet[i]);
8595             String fullName = parent.resolve(fname);
8596             //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
8597             if (isNewerThan(fullName, fullOut))
8598                 doit = true;
8599             }
8600         //trace("Needs it:%d", doit);
8601         if (!doit)
8602             {
8603             return true;
8604             }
8606         String cmd = "dllwrap";
8607         cmd.append(" -o ");
8608         cmd.append(fullOut);
8609         if (defFileName.size()>0)
8610             {
8611             cmd.append(" --def ");
8612             cmd.append(defFileName);
8613             cmd.append(" ");
8614             }
8615         if (impFileName.size()>0)
8616             {
8617             cmd.append(" --implib ");
8618             cmd.append(impFileName);
8619             cmd.append(" ");
8620             }
8621         for (unsigned int i=0 ; i<fileSet.size() ; i++)
8622             {
8623             String fname;
8624             if (fileSetDir.size()>0)
8625                 {
8626                 fname.append(fileSetDir);
8627                 fname.append("/");
8628                 }
8629             fname.append(fileSet[i]);
8630             String fullName = parent.resolve(fname);
8632             cmd.append(" ");
8633             cmd.append(fullName);
8634             }
8635         cmd.append(" ");
8636         cmd.append(libs);
8638         String outString, errString;
8639         if (!executeCommand(cmd.c_str(), "", outString, errString))
8640             {
8641             error("<sharedlib> problem: %s", errString.c_str());
8642             return false;
8643             }
8644         removeFromStatCache(getNativePath(fullOut));
8645         return true;
8646         }
8648     virtual bool parse(Element *elem)
8649         {
8650         if (!parent.getAttribute(elem, "command", commandOpt))
8651             return false;
8652         if (!parent.getAttribute(elem, "file", fileNameOpt))
8653             return false;
8654         if (!parent.getAttribute(elem, "import", impFileNameOpt))
8655             return false;
8656         if (!parent.getAttribute(elem, "def", defFileNameOpt))
8657             return false;
8658             
8659         std::vector<Element *> children = elem->getChildren();
8660         for (unsigned int i=0 ; i<children.size() ; i++)
8661             {
8662             Element *child = children[i];
8663             String tagName = child->getName();
8664             if (tagName == "fileset")
8665                 {
8666                 if (!parseFileSet(child, parent, fileSet))
8667                     return false;
8668                 }
8669             else if (tagName == "libs")
8670                 {
8671                 if (!parent.getValue(child, libsOpt))
8672                     return false;
8673                 libsOpt = strip(libsOpt);
8674                 }
8675             }
8676         return true;
8677         }
8679 private:
8681     FileSet fileSet;
8683     String commandOpt;
8684     String fileNameOpt;
8685     String defFileNameOpt;
8686     String impFileNameOpt;
8687     String libsOpt;
8689 };
8693 /**
8694  * Run the "ar" command to archive .o's into a .a
8695  */
8696 class TaskStaticLib : public Task
8698 public:
8700     TaskStaticLib(MakeBase &par) : Task(par)
8701         { type = TASK_STATICLIB; name = "staticlib"; }
8703     virtual ~TaskStaticLib()
8704         {}
8706     virtual bool execute()
8707         {
8708         String command = parent.eval(commandOpt, "ar crv");
8709         String fileName = parent.eval(fileNameOpt, "");
8711         bool doit = false;
8712         
8713         String fullOut = parent.resolve(fileName);
8714         //trace("ar fullout: %s", fullOut.c_str());
8715         
8716         if (!listFiles(parent, fileSet))
8717             return false;
8718         String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
8719         //trace("###########HERE %s", fileSetDir.c_str());
8721         for (unsigned int i=0 ; i<fileSet.size() ; i++)
8722             {
8723             String fname;
8724             if (fileSetDir.size()>0)
8725                 {
8726                 fname.append(fileSetDir);
8727                 fname.append("/");
8728                 }
8729             fname.append(fileSet[i]);
8730             String fullName = parent.resolve(fname);
8731             //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
8732             if (isNewerThan(fullName, fullOut))
8733                 doit = true;
8734             }
8735         //trace("Needs it:%d", doit);
8736         if (!doit)
8737             {
8738             return true;
8739             }
8741         String cmd = command;
8742         cmd.append(" ");
8743         cmd.append(fullOut);
8744         for (unsigned int i=0 ; i<fileSet.size() ; i++)
8745             {
8746             String fname;
8747             if (fileSetDir.size()>0)
8748                 {
8749                 fname.append(fileSetDir);
8750                 fname.append("/");
8751                 }
8752             fname.append(fileSet[i]);
8753             String fullName = parent.resolve(fname);
8755             cmd.append(" ");
8756             cmd.append(fullName);
8757             }
8759         String outString, errString;
8760         if (!executeCommand(cmd.c_str(), "", outString, errString))
8761             {
8762             error("<staticlib> problem: %s", errString.c_str());
8763             return false;
8764             }
8765         removeFromStatCache(getNativePath(fullOut));
8766         return true;
8767         }
8770     virtual bool parse(Element *elem)
8771         {
8772         if (!parent.getAttribute(elem, "command", commandOpt))
8773             return false;
8774         if (!parent.getAttribute(elem, "file", fileNameOpt))
8775             return false;
8776             
8777         std::vector<Element *> children = elem->getChildren();
8778         for (unsigned int i=0 ; i<children.size() ; i++)
8779             {
8780             Element *child = children[i];
8781             String tagName = child->getName();
8782             if (tagName == "fileset")
8783                 {
8784                 if (!parseFileSet(child, parent, fileSet))
8785                     return false;
8786                 }
8787             }
8788         return true;
8789         }
8791 private:
8793     FileSet fileSet;
8795     String commandOpt;
8796     String fileNameOpt;
8798 };
8803 /**
8804  * Strip an executable
8805  */
8806 class TaskStrip : public Task
8808 public:
8810     TaskStrip(MakeBase &par) : Task(par)
8811         { type = TASK_STRIP; name = "strip"; }
8813     virtual ~TaskStrip()
8814         {}
8816     virtual bool execute()
8817         {
8818         String command     = parent.eval(commandOpt, "strip");
8819         String fileName    = parent.eval(fileNameOpt, "");
8820         String symFileName = parent.eval(symFileNameOpt, "");
8822         String fullName = parent.resolve(fileName);
8823         //trace("fullDir:%s", fullDir.c_str());
8824         String cmd;
8825         String outbuf, errbuf;
8827         if (symFileName.size()>0)
8828             {
8829             String symFullName = parent.resolve(symFileName);
8830             cmd = "objcopy --only-keep-debug ";
8831             cmd.append(getNativePath(fullName));
8832             cmd.append(" ");
8833             cmd.append(getNativePath(symFullName));
8834             if (!executeCommand(cmd, "", outbuf, errbuf))
8835                 {
8836                 error("<strip> symbol file failed : %s", errbuf.c_str());
8837                 return false;
8838                 }
8839             }
8840             
8841         cmd = command;
8842         cmd.append(getNativePath(fullName));
8843         if (!executeCommand(cmd, "", outbuf, errbuf))
8844             {
8845             error("<strip> failed : %s", errbuf.c_str());
8846             return false;
8847             }
8848         removeFromStatCache(getNativePath(fullName));
8849         return true;
8850         }
8852     virtual bool parse(Element *elem)
8853         {
8854         if (!parent.getAttribute(elem, "command", commandOpt))
8855             return false;
8856         if (!parent.getAttribute(elem, "file", fileNameOpt))
8857             return false;
8858         if (!parent.getAttribute(elem, "symfile", symFileNameOpt))
8859             return false;
8860         if (fileNameOpt.size() == 0)
8861             {
8862             error("<strip> requires 'file=\"fileName\"' attribute");
8863             return false;
8864             }
8865         return true;
8866         }
8868 private:
8870     String commandOpt;
8871     String fileNameOpt;
8872     String symFileNameOpt;
8873 };
8876 /**
8877  *
8878  */
8879 class TaskTouch : public Task
8881 public:
8883     TaskTouch(MakeBase &par) : Task(par)
8884         { type = TASK_TOUCH; name = "touch"; }
8886     virtual ~TaskTouch()
8887         {}
8889     virtual bool execute()
8890         {
8891         String fileName = parent.eval(fileNameOpt, "");
8893         String fullName = parent.resolve(fileName);
8894         String nativeFile = getNativePath(fullName);
8895         if (!isRegularFile(fullName) && !isDirectory(fullName))
8896             {            
8897             // S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH
8898             int ret = creat(nativeFile.c_str(), 0666);
8899             if (ret != 0) 
8900                 {
8901                 error("<touch> could not create '%s' : %s",
8902                     nativeFile.c_str(), strerror(ret));
8903                 return false;
8904                 }
8905             return true;
8906             }
8907         int ret = utime(nativeFile.c_str(), (struct utimbuf *)0);
8908         if (ret != 0)
8909             {
8910             error("<touch> could not update the modification time for '%s' : %s",
8911                 nativeFile.c_str(), strerror(ret));
8912             return false;
8913             }
8914         removeFromStatCache(nativeFile);
8915         return true;
8916         }
8918     virtual bool parse(Element *elem)
8919         {
8920         //trace("touch parse");
8921         if (!parent.getAttribute(elem, "file", fileNameOpt))
8922             return false;
8923         if (fileNameOpt.size() == 0)
8924             {
8925             error("<touch> requires 'file=\"fileName\"' attribute");
8926             return false;
8927             }
8928         return true;
8929         }
8931     String fileNameOpt;
8932 };
8935 /**
8936  *
8937  */
8938 class TaskTstamp : public Task
8940 public:
8942     TaskTstamp(MakeBase &par) : Task(par)
8943         { type = TASK_TSTAMP; name = "tstamp"; }
8945     virtual ~TaskTstamp()
8946         {}
8948     virtual bool execute()
8949         {
8950         return true;
8951         }
8953     virtual bool parse(Element *elem)
8954         {
8955         //trace("tstamp parse");
8956         return true;
8957         }
8958 };
8962 /**
8963  *
8964  */
8965 Task *Task::createTask(Element *elem, int lineNr)
8967     String tagName = elem->getName();
8968     //trace("task:%s", tagName.c_str());
8969     Task *task = NULL;
8970     if (tagName == "cc")
8971         task = new TaskCC(parent);
8972     else if (tagName == "copy")
8973         task = new TaskCopy(parent);
8974     else if (tagName == "cxxtestpart")
8975         task = new TaskCxxTestPart(parent);
8976     else if (tagName == "cxxtestroot")
8977         task = new TaskCxxTestRoot(parent);
8978     else if (tagName == "cxxtestrun")
8979         task = new TaskCxxTestRun(parent);
8980     else if (tagName == "delete")
8981         task = new TaskDelete(parent);
8982     else if (tagName == "echo")
8983         task = new TaskEcho(parent);
8984     else if (tagName == "jar")
8985         task = new TaskJar(parent);
8986     else if (tagName == "javac")
8987         task = new TaskJavac(parent);
8988     else if (tagName == "link")
8989         task = new TaskLink(parent);
8990     else if (tagName == "makefile")
8991         task = new TaskMakeFile(parent);
8992     else if (tagName == "mkdir")
8993         task = new TaskMkDir(parent);
8994     else if (tagName == "msgfmt")
8995         task = new TaskMsgFmt(parent);
8996     else if (tagName == "pkg-config")
8997         task = new TaskPkgConfig(parent);
8998     else if (tagName == "ranlib")
8999         task = new TaskRanlib(parent);
9000     else if (tagName == "rc")
9001         task = new TaskRC(parent);
9002     else if (tagName == "sharedlib")
9003         task = new TaskSharedLib(parent);
9004     else if (tagName == "staticlib")
9005         task = new TaskStaticLib(parent);
9006     else if (tagName == "strip")
9007         task = new TaskStrip(parent);
9008     else if (tagName == "touch")
9009         task = new TaskTouch(parent);
9010     else if (tagName == "tstamp")
9011         task = new TaskTstamp(parent);
9012     else
9013         {
9014         error("Unknown task '%s'", tagName.c_str());
9015         return NULL;
9016         }
9018     task->setLine(lineNr);
9020     if (!task->parse(elem))
9021         {
9022         delete task;
9023         return NULL;
9024         }
9025     return task;
9030 //########################################################################
9031 //# T A R G E T
9032 //########################################################################
9034 /**
9035  *
9036  */
9037 class Target : public MakeBase
9040 public:
9042     /**
9043      *
9044      */
9045     Target(Make &par) : parent(par)
9046         { init(); }
9048     /**
9049      *
9050      */
9051     Target(const Target &other) : parent(other.parent)
9052         { init(); assign(other); }
9054     /**
9055      *
9056      */
9057     Target &operator=(const Target &other)
9058         { init(); assign(other); return *this; }
9060     /**
9061      *
9062      */
9063     virtual ~Target()
9064         { cleanup() ; }
9067     /**
9068      *
9069      */
9070     virtual Make &getParent()
9071         { return parent; }
9073     /**
9074      *
9075      */
9076     virtual String getName()
9077         { return name; }
9079     /**
9080      *
9081      */
9082     virtual void setName(const String &val)
9083         { name = val; }
9085     /**
9086      *
9087      */
9088     virtual String getDescription()
9089         { return description; }
9091     /**
9092      *
9093      */
9094     virtual void setDescription(const String &val)
9095         { description = val; }
9097     /**
9098      *
9099      */
9100     virtual void addDependency(const String &val)
9101         { deps.push_back(val); }
9103     /**
9104      *
9105      */
9106     virtual void parseDependencies(const String &val)
9107         { deps = tokenize(val, ", "); }
9109     /**
9110      *
9111      */
9112     virtual std::vector<String> &getDependencies()
9113         { return deps; }
9115     /**
9116      *
9117      */
9118     virtual String getIf()
9119         { return ifVar; }
9121     /**
9122      *
9123      */
9124     virtual void setIf(const String &val)
9125         { ifVar = val; }
9127     /**
9128      *
9129      */
9130     virtual String getUnless()
9131         { return unlessVar; }
9133     /**
9134      *
9135      */
9136     virtual void setUnless(const String &val)
9137         { unlessVar = val; }
9139     /**
9140      *
9141      */
9142     virtual void addTask(Task *val)
9143         { tasks.push_back(val); }
9145     /**
9146      *
9147      */
9148     virtual std::vector<Task *> &getTasks()
9149         { return tasks; }
9151 private:
9153     void init()
9154         {
9155         }
9157     void cleanup()
9158         {
9159         tasks.clear();
9160         }
9162     void assign(const Target &other)
9163         {
9164         //parent      = other.parent;
9165         name        = other.name;
9166         description = other.description;
9167         ifVar       = other.ifVar;
9168         unlessVar   = other.unlessVar;
9169         deps        = other.deps;
9170         tasks       = other.tasks;
9171         }
9173     Make &parent;
9175     String name;
9177     String description;
9179     String ifVar;
9181     String unlessVar;
9183     std::vector<String> deps;
9185     std::vector<Task *> tasks;
9187 };
9196 //########################################################################
9197 //# M A K E
9198 //########################################################################
9201 /**
9202  *
9203  */
9204 class Make : public MakeBase
9207 public:
9209     /**
9210      *
9211      */
9212     Make()
9213         { init(); }
9215     /**
9216      *
9217      */
9218     Make(const Make &other)
9219         { assign(other); }
9221     /**
9222      *
9223      */
9224     Make &operator=(const Make &other)
9225         { assign(other); return *this; }
9227     /**
9228      *
9229      */
9230     virtual ~Make()
9231         { cleanup(); }
9233     /**
9234      *
9235      */
9236     virtual std::map<String, Target> &getTargets()
9237         { return targets; }
9240     /**
9241      *
9242      */
9243     virtual String version()
9244         { return BUILDTOOL_VERSION; }
9246     /**
9247      * Overload a <property>
9248      */
9249     virtual bool specifyProperty(const String &name,
9250                                  const String &value);
9252     /**
9253      *
9254      */
9255     virtual bool run();
9257     /**
9258      *
9259      */
9260     virtual bool run(const String &target);
9264 private:
9266     /**
9267      *
9268      */
9269     void init();
9271     /**
9272      *
9273      */
9274     void cleanup();
9276     /**
9277      *
9278      */
9279     void assign(const Make &other);
9281     /**
9282      *
9283      */
9284     bool executeTask(Task &task);
9287     /**
9288      *
9289      */
9290     bool executeTarget(Target &target,
9291              std::set<String> &targetsCompleted);
9294     /**
9295      *
9296      */
9297     bool execute();
9299     /**
9300      *
9301      */
9302     bool checkTargetDependencies(Target &prop,
9303                     std::vector<String> &depList);
9305     /**
9306      *
9307      */
9308     bool parsePropertyFile(const String &fileName,
9309                            const String &prefix);
9311     /**
9312      *
9313      */
9314     bool parseProperty(Element *elem);
9316     /**
9317      *
9318      */
9319     bool parseFile();
9321     /**
9322      *
9323      */
9324     std::vector<String> glob(const String &pattern);
9327     //###############
9328     //# Fields
9329     //###############
9331     String projectName;
9333     String currentTarget;
9335     String defaultTarget;
9337     String specifiedTarget;
9339     String baseDir;
9341     String description;
9342     
9343     //std::vector<Property> properties;
9344     
9345     std::map<String, Target> targets;
9347     std::vector<Task *> allTasks;
9348     
9349     std::map<String, String> specifiedProperties;
9351 };
9354 //########################################################################
9355 //# C L A S S  M A I N T E N A N C E
9356 //########################################################################
9358 /**
9359  *
9360  */
9361 void Make::init()
9363     uri             = "build.xml";
9364     projectName     = "";
9365     currentTarget   = "";
9366     defaultTarget   = "";
9367     specifiedTarget = "";
9368     baseDir         = "";
9369     description     = "";
9370     envPrefix       = "env.";
9371     pcPrefix        = "pc.";
9372     pccPrefix       = "pcc.";
9373     pclPrefix       = "pcl.";
9374     properties.clear();
9375     for (unsigned int i = 0 ; i < allTasks.size() ; i++)
9376         delete allTasks[i];
9377     allTasks.clear();
9382 /**
9383  *
9384  */
9385 void Make::cleanup()
9387     for (unsigned int i = 0 ; i < allTasks.size() ; i++)
9388         delete allTasks[i];
9389     allTasks.clear();
9394 /**
9395  *
9396  */
9397 void Make::assign(const Make &other)
9399     uri              = other.uri;
9400     projectName      = other.projectName;
9401     currentTarget    = other.currentTarget;
9402     defaultTarget    = other.defaultTarget;
9403     specifiedTarget  = other.specifiedTarget;
9404     baseDir          = other.baseDir;
9405     description      = other.description;
9406     properties       = other.properties;
9411 //########################################################################
9412 //# U T I L I T Y    T A S K S
9413 //########################################################################
9415 /**
9416  *  Perform a file globbing
9417  */
9418 std::vector<String> Make::glob(const String &pattern)
9420     std::vector<String> res;
9421     return res;
9425 //########################################################################
9426 //# P U B L I C    A P I
9427 //########################################################################
9431 /**
9432  *
9433  */
9434 bool Make::executeTarget(Target &target,
9435              std::set<String> &targetsCompleted)
9438     String name = target.getName();
9440     //First get any dependencies for this target
9441     std::vector<String> deps = target.getDependencies();
9442     for (unsigned int i=0 ; i<deps.size() ; i++)
9443         {
9444         String dep = deps[i];
9445         //Did we do it already?  Skip
9446         if (targetsCompleted.find(dep)!=targetsCompleted.end())
9447             continue;
9448             
9449         std::map<String, Target> &tgts =
9450                target.getParent().getTargets();
9451         std::map<String, Target>::iterator iter =
9452                tgts.find(dep);
9453         if (iter == tgts.end())
9454             {
9455             error("Target '%s' dependency '%s' not found",
9456                       name.c_str(),  dep.c_str());
9457             return false;
9458             }
9459         Target depTarget = iter->second;
9460         if (!executeTarget(depTarget, targetsCompleted))
9461             {
9462             return false;
9463             }
9464         }
9466     status("##### Target : %s\n##### %s", name.c_str(),
9467             target.getDescription().c_str());
9469     //Now let's do the tasks
9470     std::vector<Task *> &tasks = target.getTasks();
9471     for (unsigned int i=0 ; i<tasks.size() ; i++)
9472         {
9473         Task *task = tasks[i];
9474         status("--- %s / %s", name.c_str(), task->getName().c_str());
9475         if (!task->execute())
9476             {
9477             return false;
9478             }
9479         }
9480         
9481     targetsCompleted.insert(name);
9482     
9483     return true;
9488 /**
9489  *  Main execute() method.  Start here and work
9490  *  up the dependency tree 
9491  */
9492 bool Make::execute()
9494     status("######## EXECUTE");
9496     //Determine initial target
9497     if (specifiedTarget.size()>0)
9498         {
9499         currentTarget = specifiedTarget;
9500         }
9501     else if (defaultTarget.size()>0)
9502         {
9503         currentTarget = defaultTarget;
9504         }
9505     else
9506         {
9507         error("execute: no specified or default target requested");
9508         return false;
9509         }
9511     std::map<String, Target>::iterator iter =
9512                targets.find(currentTarget);
9513     if (iter == targets.end())
9514         {
9515         error("Initial target '%s' not found",
9516                  currentTarget.c_str());
9517         return false;
9518         }
9519         
9520     //Now run
9521     Target target = iter->second;
9522     std::set<String> targetsCompleted;
9523     if (!executeTarget(target, targetsCompleted))
9524         {
9525         return false;
9526         }
9528     status("######## EXECUTE COMPLETE");
9529     return true;
9535 /**
9536  *
9537  */
9538 bool Make::checkTargetDependencies(Target &target, 
9539                             std::vector<String> &depList)
9541     String tgtName = target.getName().c_str();
9542     depList.push_back(tgtName);
9544     std::vector<String> deps = target.getDependencies();
9545     for (unsigned int i=0 ; i<deps.size() ; i++)
9546         {
9547         String dep = deps[i];
9548         //First thing entered was the starting Target
9549         if (dep == depList[0])
9550             {
9551             error("Circular dependency '%s' found at '%s'",
9552                       dep.c_str(), tgtName.c_str());
9553             std::vector<String>::iterator diter;
9554             for (diter=depList.begin() ; diter!=depList.end() ; diter++)
9555                 {
9556                 error("  %s", diter->c_str());
9557                 }
9558             return false;
9559             }
9561         std::map<String, Target> &tgts =
9562                   target.getParent().getTargets();
9563         std::map<String, Target>::iterator titer = tgts.find(dep);
9564         if (titer == tgts.end())
9565             {
9566             error("Target '%s' dependency '%s' not found",
9567                       tgtName.c_str(), dep.c_str());
9568             return false;
9569             }
9570         if (!checkTargetDependencies(titer->second, depList))
9571             {
9572             return false;
9573             }
9574         }
9575     return true;
9582 static int getword(int pos, const String &inbuf, String &result)
9584     int p = pos;
9585     int len = (int)inbuf.size();
9586     String val;
9587     while (p < len)
9588         {
9589         char ch = inbuf[p];
9590         if (!isalnum(ch) && ch!='.' && ch!='_')
9591             break;
9592         val.push_back(ch);
9593         p++;
9594         }
9595     result = val;
9596     return p;
9602 /**
9603  *
9604  */
9605 bool Make::parsePropertyFile(const String &fileName,
9606                              const String &prefix)
9608     FILE *f = fopen(fileName.c_str(), "r");
9609     if (!f)
9610         {
9611         error("could not open property file %s", fileName.c_str());
9612         return false;
9613         }
9614     int linenr = 0;
9615     while (!feof(f))
9616         {
9617         char buf[256];
9618         if (!fgets(buf, 255, f))
9619             break;
9620         linenr++;
9621         String s = buf;
9622         s = trim(s);
9623         int len = s.size();
9624         if (len == 0)
9625             continue;
9626         if (s[0] == '#')
9627             continue;
9628         String key;
9629         String val;
9630         int p = 0;
9631         int p2 = getword(p, s, key);
9632         if (p2 <= p)
9633             {
9634             error("property file %s, line %d: expected keyword",
9635                     fileName.c_str(), linenr);
9636             return false;
9637             }
9638         if (prefix.size() > 0)
9639             {
9640             key.insert(0, prefix);
9641             }
9643         //skip whitespace
9644         for (p=p2 ; p<len ; p++)
9645             if (!isspace(s[p]))
9646                 break;
9648         if (p>=len || s[p]!='=')
9649             {
9650             error("property file %s, line %d: expected '='",
9651                     fileName.c_str(), linenr);
9652             return false;
9653             }
9654         p++;
9656         //skip whitespace
9657         for ( ; p<len ; p++)
9658             if (!isspace(s[p]))
9659                 break;
9661         /* This way expects a word after the =
9662         p2 = getword(p, s, val);
9663         if (p2 <= p)
9664             {
9665             error("property file %s, line %d: expected value",
9666                     fileName.c_str(), linenr);
9667             return false;
9668             }
9669         */
9670         // This way gets the rest of the line after the =
9671         if (p>=len)
9672             {
9673             error("property file %s, line %d: expected value",
9674                     fileName.c_str(), linenr);
9675             return false;
9676             }
9677         val = s.substr(p);
9678         if (key.size()==0)
9679             continue;
9680         //allow property to be set, even if val=""
9682         //trace("key:'%s' val:'%s'", key.c_str(), val.c_str());
9683         //See if we wanted to overload this property
9684         std::map<String, String>::iterator iter =
9685             specifiedProperties.find(key);
9686         if (iter!=specifiedProperties.end())
9687             {
9688             val = iter->second;
9689             status("overloading property '%s' = '%s'",
9690                    key.c_str(), val.c_str());
9691             }
9692         properties[key] = val;
9693         }
9694     fclose(f);
9695     return true;
9701 /**
9702  *
9703  */
9704 bool Make::parseProperty(Element *elem)
9706     std::vector<Attribute> &attrs = elem->getAttributes();
9707     for (unsigned int i=0 ; i<attrs.size() ; i++)
9708         {
9709         String attrName = attrs[i].getName();
9710         String attrVal  = attrs[i].getValue();
9712         if (attrName == "name")
9713             {
9714             String val;
9715             if (!getAttribute(elem, "value", val))
9716                 return false;
9717             if (val.size() > 0)
9718                 {
9719                 properties[attrVal] = val;
9720                 }
9721             else
9722                 {
9723                 if (!getAttribute(elem, "location", val))
9724                     return false;
9725                 //let the property exist, even if not defined
9726                 properties[attrVal] = val;
9727                 }
9728             //See if we wanted to overload this property
9729             std::map<String, String>::iterator iter =
9730                 specifiedProperties.find(attrVal);
9731             if (iter != specifiedProperties.end())
9732                 {
9733                 val = iter->second;
9734                 status("overloading property '%s' = '%s'",
9735                     attrVal.c_str(), val.c_str());
9736                 properties[attrVal] = val;
9737                 }
9738             }
9739         else if (attrName == "file")
9740             {
9741             String prefix;
9742             if (!getAttribute(elem, "prefix", prefix))
9743                 return false;
9744             if (prefix.size() > 0)
9745                 {
9746                 if (prefix[prefix.size()-1] != '.')
9747                     prefix.push_back('.');
9748                 }
9749             if (!parsePropertyFile(attrName, prefix))
9750                 return false;
9751             }
9752         else if (attrName == "environment")
9753             {
9754             if (attrVal.find('.') != attrVal.npos)
9755                 {
9756                 error("environment prefix cannot have a '.' in it");
9757                 return false;
9758                 }
9759             envPrefix = attrVal;
9760             envPrefix.push_back('.');
9761             }
9762         else if (attrName == "pkg-config")
9763             {
9764             if (attrVal.find('.') != attrVal.npos)
9765                 {
9766                 error("pkg-config prefix cannot have a '.' in it");
9767                 return false;
9768                 }
9769             pcPrefix = attrVal;
9770             pcPrefix.push_back('.');
9771             }
9772         else if (attrName == "pkg-config-cflags")
9773             {
9774             if (attrVal.find('.') != attrVal.npos)
9775                 {
9776                 error("pkg-config-cflags prefix cannot have a '.' in it");
9777                 return false;
9778                 }
9779             pccPrefix = attrVal;
9780             pccPrefix.push_back('.');
9781             }
9782         else if (attrName == "pkg-config-libs")
9783             {
9784             if (attrVal.find('.') != attrVal.npos)
9785                 {
9786                 error("pkg-config-libs prefix cannot have a '.' in it");
9787                 return false;
9788                 }
9789             pclPrefix = attrVal;
9790             pclPrefix.push_back('.');
9791             }
9792         }
9794     return true;
9800 /**
9801  *
9802  */
9803 bool Make::parseFile()
9805     status("######## PARSE : %s", uri.getPath().c_str());
9807     setLine(0);
9809     Parser parser;
9810     Element *root = parser.parseFile(uri.getNativePath());
9811     if (!root)
9812         {
9813         error("Could not open %s for reading",
9814               uri.getNativePath().c_str());
9815         return false;
9816         }
9817     
9818     setLine(root->getLine());
9820     if (root->getChildren().size()==0 ||
9821         root->getChildren()[0]->getName()!="project")
9822         {
9823         error("Main xml element should be <project>");
9824         delete root;
9825         return false;
9826         }
9828     //########## Project attributes
9829     Element *project = root->getChildren()[0];
9830     String s = project->getAttribute("name");
9831     if (s.size() > 0)
9832         projectName = s;
9833     s = project->getAttribute("default");
9834     if (s.size() > 0)
9835         defaultTarget = s;
9836     s = project->getAttribute("basedir");
9837     if (s.size() > 0)
9838         baseDir = s;
9840     //######### PARSE MEMBERS
9841     std::vector<Element *> children = project->getChildren();
9842     for (unsigned int i=0 ; i<children.size() ; i++)
9843         {
9844         Element *elem = children[i];
9845         setLine(elem->getLine());
9846         String tagName = elem->getName();
9848         //########## DESCRIPTION
9849         if (tagName == "description")
9850             {
9851             description = parser.trim(elem->getValue());
9852             }
9854         //######### PROPERTY
9855         else if (tagName == "property")
9856             {
9857             if (!parseProperty(elem))
9858                 return false;
9859             }
9861         //######### TARGET
9862         else if (tagName == "target")
9863             {
9864             String tname   = elem->getAttribute("name");
9865             String tdesc   = elem->getAttribute("description");
9866             String tdeps   = elem->getAttribute("depends");
9867             String tif     = elem->getAttribute("if");
9868             String tunless = elem->getAttribute("unless");
9869             Target target(*this);
9870             target.setName(tname);
9871             target.setDescription(tdesc);
9872             target.parseDependencies(tdeps);
9873             target.setIf(tif);
9874             target.setUnless(tunless);
9875             std::vector<Element *> telems = elem->getChildren();
9876             for (unsigned int i=0 ; i<telems.size() ; i++)
9877                 {
9878                 Element *telem = telems[i];
9879                 Task breeder(*this);
9880                 Task *task = breeder.createTask(telem, telem->getLine());
9881                 if (!task)
9882                     return false;
9883                 allTasks.push_back(task);
9884                 target.addTask(task);
9885                 }
9887             //Check name
9888             if (tname.size() == 0)
9889                 {
9890                 error("no name for target");
9891                 return false;
9892                 }
9893             //Check for duplicate name
9894             if (targets.find(tname) != targets.end())
9895                 {
9896                 error("target '%s' already defined", tname.c_str());
9897                 return false;
9898                 }
9899             //more work than targets[tname]=target, but avoids default allocator
9900             targets.insert(std::make_pair<String, Target>(tname, target));
9901             }
9902         //######### none of the above
9903         else
9904             {
9905             error("unknown toplevel tag: <%s>", tagName.c_str());
9906             return false;
9907             }
9909         }
9911     std::map<String, Target>::iterator iter;
9912     for (iter = targets.begin() ; iter!= targets.end() ; iter++)
9913         {
9914         Target tgt = iter->second;
9915         std::vector<String> depList;
9916         if (!checkTargetDependencies(tgt, depList))
9917             {
9918             return false;
9919             }
9920         }
9923     delete root;
9924     status("######## PARSE COMPLETE");
9925     return true;
9929 /**
9930  * Overload a <property>
9931  */
9932 bool Make::specifyProperty(const String &name, const String &value)
9934     if (specifiedProperties.find(name) != specifiedProperties.end())
9935         {
9936         error("Property %s already specified", name.c_str());
9937         return false;
9938         }
9939     specifiedProperties[name] = value;
9940     return true;
9945 /**
9946  *
9947  */
9948 bool Make::run()
9950     if (!parseFile())
9951         return false;
9952         
9953     if (!execute())
9954         return false;
9956     return true;
9962 /**
9963  * Get a formatted MM:SS.sss time elapsed string
9964  */ 
9965 static String
9966 timeDiffString(struct timeval &x, struct timeval &y)
9968     long microsX  = x.tv_usec;
9969     long secondsX = x.tv_sec;
9970     long microsY  = y.tv_usec;
9971     long secondsY = y.tv_sec;
9972     if (microsX < microsY)
9973         {
9974         microsX += 1000000;
9975         secondsX -= 1;
9976         }
9978     int seconds = (int)(secondsX - secondsY);
9979     int millis  = (int)((microsX - microsY)/1000);
9981     int minutes = seconds/60;
9982     seconds -= minutes*60;
9983     char buf[80];
9984     snprintf(buf, 79, "%dm %d.%03ds", minutes, seconds, millis);
9985     String ret = buf;
9986     return ret;
9987     
9990 /**
9991  *
9992  */
9993 bool Make::run(const String &target)
9995     status("####################################################");
9996     status("#   %s", version().c_str());
9997     status("####################################################");
9998     struct timeval timeStart, timeEnd;
9999     ::gettimeofday(&timeStart, NULL);
10000     specifiedTarget = target;
10001     if (!run())
10002         return false;
10003     ::gettimeofday(&timeEnd, NULL);
10004     String timeStr = timeDiffString(timeEnd, timeStart);
10005     status("####################################################");
10006     status("#   BuildTool Completed : %s", timeStr.c_str());
10007     status("####################################################");
10008     return true;
10017 }// namespace buildtool
10018 //########################################################################
10019 //# M A I N
10020 //########################################################################
10022 typedef buildtool::String String;
10024 /**
10025  *  Format an error message in printf() style
10026  */
10027 static void error(const char *fmt, ...)
10029     va_list ap;
10030     va_start(ap, fmt);
10031     fprintf(stderr, "BuildTool error: ");
10032     vfprintf(stderr, fmt, ap);
10033     fprintf(stderr, "\n");
10034     va_end(ap);
10038 static bool parseProperty(const String &s, String &name, String &val)
10040     int len = s.size();
10041     int i;
10042     for (i=0 ; i<len ; i++)
10043         {
10044         char ch = s[i];
10045         if (ch == '=')
10046             break;
10047         name.push_back(ch);
10048         }
10049     if (i>=len || s[i]!='=')
10050         {
10051         error("property requires -Dname=value");
10052         return false;
10053         }
10054     i++;
10055     for ( ; i<len ; i++)
10056         {
10057         char ch = s[i];
10058         val.push_back(ch);
10059         }
10060     return true;
10064 /**
10065  * Compare a buffer with a key, for the length of the key
10066  */
10067 static bool sequ(const String &buf, const char *key)
10069     int len = buf.size();
10070     for (int i=0 ; key[i] && i<len ; i++)
10071         {
10072         if (key[i] != buf[i])
10073             return false;
10074         }        
10075     return true;
10078 static void usage(int argc, char **argv)
10080     printf("usage:\n");
10081     printf("   %s [options] [target]\n", argv[0]);
10082     printf("Options:\n");
10083     printf("  -help, -h              print this message\n");
10084     printf("  -version               print the version information and exit\n");
10085     printf("  -file <file>           use given buildfile\n");
10086     printf("  -f <file>                 ''\n");
10087     printf("  -D<property>=<value>   use value for given property\n");
10093 /**
10094  * Parse the command-line args, get our options,
10095  * and run this thing
10096  */   
10097 static bool parseOptions(int argc, char **argv)
10099     if (argc < 1)
10100         {
10101         error("Cannot parse arguments");
10102         return false;
10103         }
10105     buildtool::Make make;
10107     String target;
10109     //char *progName = argv[0];
10110     for (int i=1 ; i<argc ; i++)
10111         {
10112         String arg = argv[i];
10113         if (arg.size()>1 && arg[0]=='-')
10114             {
10115             if (arg == "-h" || arg == "-help")
10116                 {
10117                 usage(argc,argv);
10118                 return true;
10119                 }
10120             else if (arg == "-version")
10121                 {
10122                 printf("%s", make.version().c_str());
10123                 return true;
10124                 }
10125             else if (arg == "-f" || arg == "-file")
10126                 {
10127                 if (i>=argc)
10128                    {
10129                    usage(argc, argv);
10130                    return false;
10131                    }
10132                 i++; //eat option
10133                 make.setURI(argv[i]);
10134                 }
10135             else if (arg.size()>2 && sequ(arg, "-D"))
10136                 {
10137                 String s = arg.substr(2, arg.size());
10138                 String name, value;
10139                 if (!parseProperty(s, name, value))
10140                    {
10141                    usage(argc, argv);
10142                    return false;
10143                    }
10144                 if (!make.specifyProperty(name, value))
10145                     return false;
10146                 }
10147             else
10148                 {
10149                 error("Unknown option:%s", arg.c_str());
10150                 return false;
10151                 }
10152             }
10153         else
10154             {
10155             if (target.size()>0)
10156                 {
10157                 error("only one initial target");
10158                 usage(argc, argv);
10159                 return false;
10160                 }
10161             target = arg;
10162             }
10163         }
10165     //We have the options.  Now execute them
10166     if (!make.run(target))
10167         return false;
10169     return true;
10176 static bool runMake()
10178     buildtool::Make make;
10179     if (!make.run())
10180         return false;
10181     return true;
10185 static bool pkgConfigTest()
10187     buildtool::PkgConfig pkgConfig;
10188     if (!pkgConfig.readFile("gtk+-2.0.pc"))
10189         return false;
10190     return true;
10195 static bool depTest()
10197     buildtool::DepTool deptool;
10198     deptool.setSourceDirectory("/dev/ink/inkscape/src");
10199     if (!deptool.generateDependencies("build.dep"))
10200         return false;
10201     std::vector<buildtool::FileRec> res =
10202            deptool.loadDepFile("build.dep");
10203     if (res.size() == 0)
10204         return false;
10205     return true;
10208 static bool popenTest()
10210     buildtool::Make make;
10211     buildtool::String out, err;
10212     bool ret = make.executeCommand("gcc xx.cpp", "", out, err);
10213     printf("Popen test:%d '%s' '%s'\n", ret, out.c_str(), err.c_str());
10214     return true;
10218 static bool propFileTest()
10220     buildtool::Make make;
10221     make.parsePropertyFile("test.prop", "test.");
10222     return true;
10226 int main(int argc, char **argv)
10229     if (!parseOptions(argc, argv))
10230         return 1;
10231     /*
10232     if (!popenTest())
10233         return 1;
10235     if (!depTest())
10236         return 1;
10237     if (!propFileTest())
10238         return 1;
10239     if (runMake())
10240         return 1;
10241     */
10242     return 0;
10246 //########################################################################
10247 //# E N D 
10248 //########################################################################