Code

Fixed svg-path (and display/curve) tests to properly handle closepath and made a...
[inkscape.git] / buildtool.cpp
1 /**
2  * Simple build automation tool.
3  *
4  * Authors:
5  *   Bob Jamison
6  *   Jasper van de Gronde
7  *
8  * Copyright (C) 2006-2008 Bob Jamison
9  *
10  *  This library is free software; you can redistribute it and/or
11  *  modify it under the terms of the GNU Lesser General Public
12  *  License as published by the Free Software Foundation; either
13  *  version 2.1 of the License, or (at your option) any later version.
14  *
15  *  This library is distributed in the hope that it will be useful,
16  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  *  Lesser General Public License for more details.
19  *
20  *  You should have received a copy of the GNU Lesser General Public
21  *  License along with this library; if not, write to the Free Software
22  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
23  */
25 /**
26  * To use this file, compile with:
27  * <pre>
28  * g++ -O3 buildtool.cpp -o btool.exe
29  * (or whatever your compiler might be)
30  * Then
31  * btool
32  * or
33  * btool {target}
34  *
35  * Note: if you are using MinGW, and a not very recent version of it,
36  * gettimeofday() might be missing.  If so, just build this file with
37  * this command:
38  * g++ -O3 -DNEED_GETTIMEOFDAY buildtool.cpp -o btool.exe
39  *
40  */
42 #define BUILDTOOL_VERSION  "BuildTool v0.9.5"
44 #include <stdio.h>
45 #include <fcntl.h>
46 #include <unistd.h>
47 #include <stdarg.h>
48 #include <sys/stat.h>
49 #include <time.h>
50 #include <sys/time.h>
51 #include <utime.h>
52 #include <dirent.h>
54 #include <string>
55 #include <map>
56 #include <set>
57 #include <vector>
58 #include <algorithm>
61 #ifdef __WIN32__
62 #include <windows.h>
63 #endif
66 #include <errno.h>
69 //########################################################################
70 //# Definition of gettimeofday() for those who don't have it
71 //########################################################################
72 #ifdef NEED_GETTIMEOFDAY
73 #include <sys/timeb.h>
75 struct timezone {
76       int tz_minuteswest; /* minutes west of Greenwich */
77       int tz_dsttime;     /* type of dst correction */
78     };
80 static int gettimeofday (struct timeval *tv, struct timezone *tz)
81 {
82    struct _timeb tb;
84    if (!tv)
85       return (-1);
87     _ftime (&tb);
88     tv->tv_sec  = tb.time;
89     tv->tv_usec = tb.millitm * 1000 + 500;
90     if (tz)
91         {
92         tz->tz_minuteswest = -60 * _timezone;
93         tz->tz_dsttime = _daylight;
94         }
95     return 0;
96 }
98 #endif
106 namespace buildtool
112 //########################################################################
113 //########################################################################
114 //##  R E G E X P
115 //########################################################################
116 //########################################################################
118 /**
119  * This is the T-Rex regular expression library, which we
120  * gratefully acknowledge.  It's clean code and small size allow
121  * us to embed it in BuildTool without adding a dependency
122  *
123  */    
125 //begin trex.h
127 #ifndef _TREX_H_
128 #define _TREX_H_
129 /***************************************************************
130     T-Rex a tiny regular expression library
132     Copyright (C) 2003-2006 Alberto Demichelis
134     This software is provided 'as-is', without any express 
135     or implied warranty. In no event will the authors be held 
136     liable for any damages arising from the use of this software.
138     Permission is granted to anyone to use this software for 
139     any purpose, including commercial applications, and to alter
140     it and redistribute it freely, subject to the following restrictions:
142         1. The origin of this software must not be misrepresented;
143         you must not claim that you wrote the original software.
144         If you use this software in a product, an acknowledgment
145         in the product documentation would be appreciated but
146         is not required.
148         2. Altered source versions must be plainly marked as such,
149         and must not be misrepresented as being the original software.
151         3. This notice may not be removed or altered from any
152         source distribution.
154 ****************************************************************/
156 #ifdef _UNICODE
157 #define TRexChar unsigned short
158 #define MAX_CHAR 0xFFFF
159 #define _TREXC(c) L##c 
160 #define trex_strlen wcslen
161 #define trex_printf wprintf
162 #else
163 #define TRexChar char
164 #define MAX_CHAR 0xFF
165 #define _TREXC(c) (c) 
166 #define trex_strlen strlen
167 #define trex_printf printf
168 #endif
170 #ifndef TREX_API
171 #define TREX_API extern
172 #endif
174 #define TRex_True 1
175 #define TRex_False 0
177 typedef unsigned int TRexBool;
178 typedef struct TRex TRex;
180 typedef struct {
181     const TRexChar *begin;
182     int len;
183 } TRexMatch;
185 TREX_API TRex *trex_compile(const TRexChar *pattern,const TRexChar **error);
186 TREX_API void trex_free(TRex *exp);
187 TREX_API TRexBool trex_match(TRex* exp,const TRexChar* text);
188 TREX_API TRexBool trex_search(TRex* exp,const TRexChar* text, const TRexChar** out_begin, const TRexChar** out_end);
189 TREX_API TRexBool trex_searchrange(TRex* exp,const TRexChar* text_begin,const TRexChar* text_end,const TRexChar** out_begin, const TRexChar** out_end);
190 TREX_API int trex_getsubexpcount(TRex* exp);
191 TREX_API TRexBool trex_getsubexp(TRex* exp, int n, TRexMatch *subexp);
193 #endif
195 //end trex.h
197 //start trex.c
200 #include <stdio.h>
201 #include <string>
203 /* see copyright notice in trex.h */
204 #include <string.h>
205 #include <stdlib.h>
206 #include <ctype.h>
207 #include <setjmp.h>
208 //#include "trex.h"
210 #ifdef _UNICODE
211 #define scisprint iswprint
212 #define scstrlen wcslen
213 #define scprintf wprintf
214 #define _SC(x) L(x)
215 #else
216 #define scisprint isprint
217 #define scstrlen strlen
218 #define scprintf printf
219 #define _SC(x) (x)
220 #endif
222 #ifdef _DEBUG
223 #include <stdio.h>
225 static const TRexChar *g_nnames[] =
227     _SC("NONE"),_SC("OP_GREEDY"),    _SC("OP_OR"),
228     _SC("OP_EXPR"),_SC("OP_NOCAPEXPR"),_SC("OP_DOT"),    _SC("OP_CLASS"),
229     _SC("OP_CCLASS"),_SC("OP_NCLASS"),_SC("OP_RANGE"),_SC("OP_CHAR"),
230     _SC("OP_EOL"),_SC("OP_BOL"),_SC("OP_WB")
231 };
233 #endif
234 #define OP_GREEDY        (MAX_CHAR+1) // * + ? {n}
235 #define OP_OR            (MAX_CHAR+2)
236 #define OP_EXPR            (MAX_CHAR+3) //parentesis ()
237 #define OP_NOCAPEXPR    (MAX_CHAR+4) //parentesis (?:)
238 #define OP_DOT            (MAX_CHAR+5)
239 #define OP_CLASS        (MAX_CHAR+6)
240 #define OP_CCLASS        (MAX_CHAR+7)
241 #define OP_NCLASS        (MAX_CHAR+8) //negates class the [^
242 #define OP_RANGE        (MAX_CHAR+9)
243 #define OP_CHAR            (MAX_CHAR+10)
244 #define OP_EOL            (MAX_CHAR+11)
245 #define OP_BOL            (MAX_CHAR+12)
246 #define OP_WB            (MAX_CHAR+13)
248 #define TREX_SYMBOL_ANY_CHAR ('.')
249 #define TREX_SYMBOL_GREEDY_ONE_OR_MORE ('+')
250 #define TREX_SYMBOL_GREEDY_ZERO_OR_MORE ('*')
251 #define TREX_SYMBOL_GREEDY_ZERO_OR_ONE ('?')
252 #define TREX_SYMBOL_BRANCH ('|')
253 #define TREX_SYMBOL_END_OF_STRING ('$')
254 #define TREX_SYMBOL_BEGINNING_OF_STRING ('^')
255 #define TREX_SYMBOL_ESCAPE_CHAR ('\\')
258 typedef int TRexNodeType;
260 typedef struct tagTRexNode{
261     TRexNodeType type;
262     int left;
263     int right;
264     int next;
265 }TRexNode;
267 struct TRex{
268     const TRexChar *_eol;
269     const TRexChar *_bol;
270     const TRexChar *_p;
271     int _first;
272     int _op;
273     TRexNode *_nodes;
274     int _nallocated;
275     int _nsize;
276     int _nsubexpr;
277     TRexMatch *_matches;
278     int _currsubexp;
279     void *_jmpbuf;
280     const TRexChar **_error;
281 };
283 static int trex_list(TRex *exp);
285 static int trex_newnode(TRex *exp, TRexNodeType type)
287     TRexNode n;
288     int newid;
289     n.type = type;
290     n.next = n.right = n.left = -1;
291     if(type == OP_EXPR)
292         n.right = exp->_nsubexpr++;
293     if(exp->_nallocated < (exp->_nsize + 1)) {
294         //int oldsize = exp->_nallocated;
295         exp->_nallocated *= 2;
296         exp->_nodes = (TRexNode *)realloc(exp->_nodes, exp->_nallocated * sizeof(TRexNode));
297     }
298     exp->_nodes[exp->_nsize++] = n;
299     newid = exp->_nsize - 1;
300     return (int)newid;
303 static void trex_error(TRex *exp,const TRexChar *error)
305     if(exp->_error) *exp->_error = error;
306     longjmp(*((jmp_buf*)exp->_jmpbuf),-1);
309 static void trex_expect(TRex *exp, int n){
310     if((*exp->_p) != n) 
311         trex_error(exp, _SC("expected paren"));
312     exp->_p++;
315 static TRexChar trex_escapechar(TRex *exp)
317     if(*exp->_p == TREX_SYMBOL_ESCAPE_CHAR){
318         exp->_p++;
319         switch(*exp->_p) {
320         case 'v': exp->_p++; return '\v';
321         case 'n': exp->_p++; return '\n';
322         case 't': exp->_p++; return '\t';
323         case 'r': exp->_p++; return '\r';
324         case 'f': exp->_p++; return '\f';
325         default: return (*exp->_p++);
326         }
327     } else if(!scisprint(*exp->_p)) trex_error(exp,_SC("letter expected"));
328     return (*exp->_p++);
331 static int trex_charclass(TRex *exp,int classid)
333     int n = trex_newnode(exp,OP_CCLASS);
334     exp->_nodes[n].left = classid;
335     return n;
338 static int trex_charnode(TRex *exp,TRexBool isclass)
340     TRexChar t;
341     if(*exp->_p == TREX_SYMBOL_ESCAPE_CHAR) {
342         exp->_p++;
343         switch(*exp->_p) {
344             case 'n': exp->_p++; return trex_newnode(exp,'\n');
345             case 't': exp->_p++; return trex_newnode(exp,'\t');
346             case 'r': exp->_p++; return trex_newnode(exp,'\r');
347             case 'f': exp->_p++; return trex_newnode(exp,'\f');
348             case 'v': exp->_p++; return trex_newnode(exp,'\v');
349             case 'a': case 'A': case 'w': case 'W': case 's': case 'S': 
350             case 'd': case 'D': case 'x': case 'X': case 'c': case 'C': 
351             case 'p': case 'P': case 'l': case 'u': 
352                 {
353                 t = *exp->_p; exp->_p++; 
354                 return trex_charclass(exp,t);
355                 }
356             case 'b': 
357             case 'B':
358                 if(!isclass) {
359                     int node = trex_newnode(exp,OP_WB);
360                     exp->_nodes[node].left = *exp->_p;
361                     exp->_p++; 
362                     return node;
363                 } //else default
364             default: 
365                 t = *exp->_p; exp->_p++; 
366                 return trex_newnode(exp,t);
367         }
368     }
369     else if(!scisprint(*exp->_p)) {
370         
371         trex_error(exp,_SC("letter expected"));
372     }
373     t = *exp->_p; exp->_p++; 
374     return trex_newnode(exp,t);
376 static int trex_class(TRex *exp)
378     int ret = -1;
379     int first = -1,chain;
380     if(*exp->_p == TREX_SYMBOL_BEGINNING_OF_STRING){
381         ret = trex_newnode(exp,OP_NCLASS);
382         exp->_p++;
383     }else ret = trex_newnode(exp,OP_CLASS);
384     
385     if(*exp->_p == ']') trex_error(exp,_SC("empty class"));
386     chain = ret;
387     while(*exp->_p != ']' && exp->_p != exp->_eol) {
388         if(*exp->_p == '-' && first != -1){ 
389             int r,t;
390             if(*exp->_p++ == ']') trex_error(exp,_SC("unfinished range"));
391             r = trex_newnode(exp,OP_RANGE);
392             if(first>*exp->_p) trex_error(exp,_SC("invalid range"));
393             if(exp->_nodes[first].type == OP_CCLASS) trex_error(exp,_SC("cannot use character classes in ranges"));
394             exp->_nodes[r].left = exp->_nodes[first].type;
395             t = trex_escapechar(exp);
396             exp->_nodes[r].right = t;
397             exp->_nodes[chain].next = r;
398             chain = r;
399             first = -1;
400         }
401         else{
402             if(first!=-1){
403                 int c = first;
404                 exp->_nodes[chain].next = c;
405                 chain = c;
406                 first = trex_charnode(exp,TRex_True);
407             }
408             else{
409                 first = trex_charnode(exp,TRex_True);
410             }
411         }
412     }
413     if(first!=-1){
414         int c = first;
415         exp->_nodes[chain].next = c;
416         chain = c;
417         first = -1;
418     }
419     /* hack? */
420     exp->_nodes[ret].left = exp->_nodes[ret].next;
421     exp->_nodes[ret].next = -1;
422     return ret;
425 static int trex_parsenumber(TRex *exp)
427     int ret = *exp->_p-'0';
428     int positions = 10;
429     exp->_p++;
430     while(isdigit(*exp->_p)) {
431         ret = ret*10+(*exp->_p++-'0');
432         if(positions==1000000000) trex_error(exp,_SC("overflow in numeric constant"));
433         positions *= 10;
434     };
435     return ret;
438 static int trex_element(TRex *exp)
440     int ret = -1;
441     switch(*exp->_p)
442     {
443     case '(': {
444         int expr,newn;
445         exp->_p++;
448         if(*exp->_p =='?') {
449             exp->_p++;
450             trex_expect(exp,':');
451             expr = trex_newnode(exp,OP_NOCAPEXPR);
452         }
453         else
454             expr = trex_newnode(exp,OP_EXPR);
455         newn = trex_list(exp);
456         exp->_nodes[expr].left = newn;
457         ret = expr;
458         trex_expect(exp,')');
459               }
460               break;
461     case '[':
462         exp->_p++;
463         ret = trex_class(exp);
464         trex_expect(exp,']');
465         break;
466     case TREX_SYMBOL_END_OF_STRING: exp->_p++; ret = trex_newnode(exp,OP_EOL);break;
467     case TREX_SYMBOL_ANY_CHAR: exp->_p++; ret = trex_newnode(exp,OP_DOT);break;
468     default:
469         ret = trex_charnode(exp,TRex_False);
470         break;
471     }
473     {
474         int op;
475         TRexBool isgreedy = TRex_False;
476         unsigned short p0 = 0, p1 = 0;
477         switch(*exp->_p){
478             case TREX_SYMBOL_GREEDY_ZERO_OR_MORE: p0 = 0; p1 = 0xFFFF; exp->_p++; isgreedy = TRex_True; break;
479             case TREX_SYMBOL_GREEDY_ONE_OR_MORE: p0 = 1; p1 = 0xFFFF; exp->_p++; isgreedy = TRex_True; break;
480             case TREX_SYMBOL_GREEDY_ZERO_OR_ONE: p0 = 0; p1 = 1; exp->_p++; isgreedy = TRex_True; break;
481             case '{':
482                 exp->_p++;
483                 if(!isdigit(*exp->_p)) trex_error(exp,_SC("number expected"));
484                 p0 = (unsigned short)trex_parsenumber(exp);
485                 /*******************************/
486                 switch(*exp->_p) {
487             case '}':
488                 p1 = p0; exp->_p++;
489                 break;
490             case ',':
491                 exp->_p++;
492                 p1 = 0xFFFF;
493                 if(isdigit(*exp->_p)){
494                     p1 = (unsigned short)trex_parsenumber(exp);
495                 }
496                 trex_expect(exp,'}');
497                 break;
498             default:
499                 trex_error(exp,_SC(", or } expected"));
500         }
501         /*******************************/
502         isgreedy = TRex_True; 
503         break;
505         }
506         if(isgreedy) {
507             int nnode = trex_newnode(exp,OP_GREEDY);
508             op = OP_GREEDY;
509             exp->_nodes[nnode].left = ret;
510             exp->_nodes[nnode].right = ((p0)<<16)|p1;
511             ret = nnode;
512         }
513     }
514     if((*exp->_p != TREX_SYMBOL_BRANCH) && (*exp->_p != ')') && (*exp->_p != TREX_SYMBOL_GREEDY_ZERO_OR_MORE) && (*exp->_p != TREX_SYMBOL_GREEDY_ONE_OR_MORE) && (*exp->_p != '\0')) {
515         int nnode = trex_element(exp);
516         exp->_nodes[ret].next = nnode;
517     }
519     return ret;
522 static int trex_list(TRex *exp)
524     int ret=-1,e;
525     if(*exp->_p == TREX_SYMBOL_BEGINNING_OF_STRING) {
526         exp->_p++;
527         ret = trex_newnode(exp,OP_BOL);
528     }
529     e = trex_element(exp);
530     if(ret != -1) {
531         exp->_nodes[ret].next = e;
532     }
533     else ret = e;
535     if(*exp->_p == TREX_SYMBOL_BRANCH) {
536         int temp,tright;
537         exp->_p++;
538         temp = trex_newnode(exp,OP_OR);
539         exp->_nodes[temp].left = ret;
540         tright = trex_list(exp);
541         exp->_nodes[temp].right = tright;
542         ret = temp;
543     }
544     return ret;
547 static TRexBool trex_matchcclass(int cclass,TRexChar c)
549     switch(cclass) {
550     case 'a': return isalpha(c)?TRex_True:TRex_False;
551     case 'A': return !isalpha(c)?TRex_True:TRex_False;
552     case 'w': return (isalnum(c) || c == '_')?TRex_True:TRex_False;
553     case 'W': return (!isalnum(c) && c != '_')?TRex_True:TRex_False;
554     case 's': return isspace(c)?TRex_True:TRex_False;
555     case 'S': return !isspace(c)?TRex_True:TRex_False;
556     case 'd': return isdigit(c)?TRex_True:TRex_False;
557     case 'D': return !isdigit(c)?TRex_True:TRex_False;
558     case 'x': return isxdigit(c)?TRex_True:TRex_False;
559     case 'X': return !isxdigit(c)?TRex_True:TRex_False;
560     case 'c': return iscntrl(c)?TRex_True:TRex_False;
561     case 'C': return !iscntrl(c)?TRex_True:TRex_False;
562     case 'p': return ispunct(c)?TRex_True:TRex_False;
563     case 'P': return !ispunct(c)?TRex_True:TRex_False;
564     case 'l': return islower(c)?TRex_True:TRex_False;
565     case 'u': return isupper(c)?TRex_True:TRex_False;
566     }
567     return TRex_False; /*cannot happen*/
570 static TRexBool trex_matchclass(TRex* exp,TRexNode *node,TRexChar c)
572     do {
573         switch(node->type) {
574             case OP_RANGE:
575                 if(c >= node->left && c <= node->right) return TRex_True;
576                 break;
577             case OP_CCLASS:
578                 if(trex_matchcclass(node->left,c)) return TRex_True;
579                 break;
580             default:
581                 if(c == node->type)return TRex_True;
582         }
583     } while((node->next != -1) && (node = &exp->_nodes[node->next]));
584     return TRex_False;
587 static const TRexChar *trex_matchnode(TRex* exp,TRexNode *node,const TRexChar *str,TRexNode *next)
589     
590     TRexNodeType type = node->type;
591     switch(type) {
592     case OP_GREEDY: {
593         //TRexNode *greedystop = (node->next != -1) ? &exp->_nodes[node->next] : NULL;
594         TRexNode *greedystop = NULL;
595         int p0 = (node->right >> 16)&0x0000FFFF, p1 = node->right&0x0000FFFF, nmaches = 0;
596         const TRexChar *s=str, *good = str;
598         if(node->next != -1) {
599             greedystop = &exp->_nodes[node->next];
600         }
601         else {
602             greedystop = next;
603         }
605         while((nmaches == 0xFFFF || nmaches < p1)) {
607             const TRexChar *stop;
608             if(!(s = trex_matchnode(exp,&exp->_nodes[node->left],s,greedystop)))
609                 break;
610             nmaches++;
611             good=s;
612             if(greedystop) {
613                 //checks that 0 matches satisfy the expression(if so skips)
614                 //if not would always stop(for instance if is a '?')
615                 if(greedystop->type != OP_GREEDY ||
616                 (greedystop->type == OP_GREEDY && ((greedystop->right >> 16)&0x0000FFFF) != 0))
617                 {
618                     TRexNode *gnext = NULL;
619                     if(greedystop->next != -1) {
620                         gnext = &exp->_nodes[greedystop->next];
621                     }else if(next && next->next != -1){
622                         gnext = &exp->_nodes[next->next];
623                     }
624                     stop = trex_matchnode(exp,greedystop,s,gnext);
625                     if(stop) {
626                         //if satisfied stop it
627                         if(p0 == p1 && p0 == nmaches) break;
628                         else if(nmaches >= p0 && p1 == 0xFFFF) break;
629                         else if(nmaches >= p0 && nmaches <= p1) break;
630                     }
631                 }
632             }
633             
634             if(s >= exp->_eol)
635                 break;
636         }
637         if(p0 == p1 && p0 == nmaches) return good;
638         else if(nmaches >= p0 && p1 == 0xFFFF) return good;
639         else if(nmaches >= p0 && nmaches <= p1) return good;
640         return NULL;
641     }
642     case OP_OR: {
643             const TRexChar *asd = str;
644             TRexNode *temp=&exp->_nodes[node->left];
645             while( (asd = trex_matchnode(exp,temp,asd,NULL)) ) {
646                 if(temp->next != -1)
647                     temp = &exp->_nodes[temp->next];
648                 else
649                     return asd;
650             }
651             asd = str;
652             temp = &exp->_nodes[node->right];
653             while( (asd = trex_matchnode(exp,temp,asd,NULL)) ) {
654                 if(temp->next != -1)
655                     temp = &exp->_nodes[temp->next];
656                 else
657                     return asd;
658             }
659             return NULL;
660             break;
661     }
662     case OP_EXPR:
663     case OP_NOCAPEXPR:{
664             TRexNode *n = &exp->_nodes[node->left];
665             const TRexChar *cur = str;
666             int capture = -1;
667             if(node->type != OP_NOCAPEXPR && node->right == exp->_currsubexp) {
668                 capture = exp->_currsubexp;
669                 exp->_matches[capture].begin = cur;
670                 exp->_currsubexp++;
671             }
672             
673             do {
674                 TRexNode *subnext = NULL;
675                 if(n->next != -1) {
676                     subnext = &exp->_nodes[n->next];
677                 }else {
678                     subnext = next;
679                 }
680                 if(!(cur = trex_matchnode(exp,n,cur,subnext))) {
681                     if(capture != -1){
682                         exp->_matches[capture].begin = 0;
683                         exp->_matches[capture].len = 0;
684                     }
685                     return NULL;
686                 }
687             } while((n->next != -1) && (n = &exp->_nodes[n->next]));
689             if(capture != -1) 
690                 exp->_matches[capture].len = cur - exp->_matches[capture].begin;
691             return cur;
692     }                 
693     case OP_WB:
694         if((str == exp->_bol && !isspace(*str))
695          || (str == exp->_eol && !isspace(*(str-1)))
696          || (!isspace(*str) && isspace(*(str+1)))
697          || (isspace(*str) && !isspace(*(str+1))) ) {
698             return (node->left == 'b')?str:NULL;
699         }
700         return (node->left == 'b')?NULL:str;
701     case OP_BOL:
702         if(str == exp->_bol) return str;
703         return NULL;
704     case OP_EOL:
705         if(str == exp->_eol) return str;
706         return NULL;
707     case OP_DOT:{
708         *str++;
709                 }
710         return str;
711     case OP_NCLASS:
712     case OP_CLASS:
713         if(trex_matchclass(exp,&exp->_nodes[node->left],*str)?(type == OP_CLASS?TRex_True:TRex_False):(type == OP_NCLASS?TRex_True:TRex_False)) {
714             *str++;
715             return str;
716         }
717         return NULL;
718     case OP_CCLASS:
719         if(trex_matchcclass(node->left,*str)) {
720             *str++;
721             return str;
722         }
723         return NULL;
724     default: /* char */
725         if(*str != node->type) return NULL;
726         *str++;
727         return str;
728     }
729     return NULL;
732 /* public api */
733 TRex *trex_compile(const TRexChar *pattern,const TRexChar **error)
735     TRex *exp = (TRex *)malloc(sizeof(TRex));
736     exp->_eol = exp->_bol = NULL;
737     exp->_p = pattern;
738     exp->_nallocated = (int)scstrlen(pattern) * sizeof(TRexChar);
739     exp->_nodes = (TRexNode *)malloc(exp->_nallocated * sizeof(TRexNode));
740     exp->_nsize = 0;
741     exp->_matches = 0;
742     exp->_nsubexpr = 0;
743     exp->_first = trex_newnode(exp,OP_EXPR);
744     exp->_error = error;
745     exp->_jmpbuf = malloc(sizeof(jmp_buf));
746     if(setjmp(*((jmp_buf*)exp->_jmpbuf)) == 0) {
747         int res = trex_list(exp);
748         exp->_nodes[exp->_first].left = res;
749         if(*exp->_p!='\0')
750             trex_error(exp,_SC("unexpected character"));
751 #ifdef _DEBUG
752         {
753             int nsize,i;
754             TRexNode *t;
755             nsize = exp->_nsize;
756             t = &exp->_nodes[0];
757             scprintf(_SC("\n"));
758             for(i = 0;i < nsize; i++) {
759                 if(exp->_nodes[i].type>MAX_CHAR)
760                     scprintf(_SC("[%02d] %10s "),i,g_nnames[exp->_nodes[i].type-MAX_CHAR]);
761                 else
762                     scprintf(_SC("[%02d] %10c "),i,exp->_nodes[i].type);
763                 scprintf(_SC("left %02d right %02d next %02d\n"),exp->_nodes[i].left,exp->_nodes[i].right,exp->_nodes[i].next);
764             }
765             scprintf(_SC("\n"));
766         }
767 #endif
768         exp->_matches = (TRexMatch *) malloc(exp->_nsubexpr * sizeof(TRexMatch));
769         memset(exp->_matches,0,exp->_nsubexpr * sizeof(TRexMatch));
770     }
771     else{
772         trex_free(exp);
773         return NULL;
774     }
775     return exp;
778 void trex_free(TRex *exp)
780     if(exp)    {
781         if(exp->_nodes) free(exp->_nodes);
782         if(exp->_jmpbuf) free(exp->_jmpbuf);
783         if(exp->_matches) free(exp->_matches);
784         free(exp);
785     }
788 TRexBool trex_match(TRex* exp,const TRexChar* text)
790     const TRexChar* res = NULL;
791     exp->_bol = text;
792     exp->_eol = text + scstrlen(text);
793     exp->_currsubexp = 0;
794     res = trex_matchnode(exp,exp->_nodes,text,NULL);
795     if(res == NULL || res != exp->_eol)
796         return TRex_False;
797     return TRex_True;
800 TRexBool trex_searchrange(TRex* exp,const TRexChar* text_begin,const TRexChar* text_end,const TRexChar** out_begin, const TRexChar** out_end)
802     const TRexChar *cur = NULL;
803     int node = exp->_first;
804     if(text_begin >= text_end) return TRex_False;
805     exp->_bol = text_begin;
806     exp->_eol = text_end;
807     do {
808         cur = text_begin;
809         while(node != -1) {
810             exp->_currsubexp = 0;
811             cur = trex_matchnode(exp,&exp->_nodes[node],cur,NULL);
812             if(!cur)
813                 break;
814             node = exp->_nodes[node].next;
815         }
816         *text_begin++;
817     } while(cur == NULL && text_begin != text_end);
819     if(cur == NULL)
820         return TRex_False;
822     --text_begin;
824     if(out_begin) *out_begin = text_begin;
825     if(out_end) *out_end = cur;
826     return TRex_True;
829 TRexBool trex_search(TRex* exp,const TRexChar* text, const TRexChar** out_begin, const TRexChar** out_end)
831     return trex_searchrange(exp,text,text + scstrlen(text),out_begin,out_end);
834 int trex_getsubexpcount(TRex* exp)
836     return exp->_nsubexpr;
839 TRexBool trex_getsubexp(TRex* exp, int n, TRexMatch *subexp)
841     if( n<0 || n >= exp->_nsubexpr) return TRex_False;
842     *subexp = exp->_matches[n];
843     return TRex_True;
847 //########################################################################
848 //########################################################################
849 //##  E N D    R E G E X P
850 //########################################################################
851 //########################################################################
857 //########################################################################
858 //########################################################################
859 //##  X M L
860 //########################################################################
861 //########################################################################
863 // Note:  This mini-dom library comes from Pedro, another little project
864 // of mine.
866 typedef std::string String;
867 typedef unsigned int XMLCh;
870 class Namespace
872 public:
873     Namespace()
874         {}
876     Namespace(const String &prefixArg, const String &namespaceURIArg)
877         {
878         prefix       = prefixArg;
879         namespaceURI = namespaceURIArg;
880         }
882     Namespace(const Namespace &other)
883         {
884         assign(other);
885         }
887     Namespace &operator=(const Namespace &other)
888         {
889         assign(other);
890         return *this;
891         }
893     virtual ~Namespace()
894         {}
896     virtual String getPrefix()
897         { return prefix; }
899     virtual String getNamespaceURI()
900         { return namespaceURI; }
902 protected:
904     void assign(const Namespace &other)
905         {
906         prefix       = other.prefix;
907         namespaceURI = other.namespaceURI;
908         }
910     String prefix;
911     String namespaceURI;
913 };
915 class Attribute
917 public:
918     Attribute()
919         {}
921     Attribute(const String &nameArg, const String &valueArg)
922         {
923         name  = nameArg;
924         value = valueArg;
925         }
927     Attribute(const Attribute &other)
928         {
929         assign(other);
930         }
932     Attribute &operator=(const Attribute &other)
933         {
934         assign(other);
935         return *this;
936         }
938     virtual ~Attribute()
939         {}
941     virtual String getName()
942         { return name; }
944     virtual String getValue()
945         { return value; }
947 protected:
949     void assign(const Attribute &other)
950         {
951         name  = other.name;
952         value = other.value;
953         }
955     String name;
956     String value;
958 };
961 class Element
963 friend class Parser;
965 public:
966     Element()
967         {
968         init();
969         }
971     Element(const String &nameArg)
972         {
973         init();
974         name   = nameArg;
975         }
977     Element(const String &nameArg, const String &valueArg)
978         {
979         init();
980         name   = nameArg;
981         value  = valueArg;
982         }
984     Element(const Element &other)
985         {
986         assign(other);
987         }
989     Element &operator=(const Element &other)
990         {
991         assign(other);
992         return *this;
993         }
995     virtual Element *clone();
997     virtual ~Element()
998         {
999         for (unsigned int i=0 ; i<children.size() ; i++)
1000             delete children[i];
1001         }
1003     virtual String getName()
1004         { return name; }
1006     virtual String getValue()
1007         { return value; }
1009     Element *getParent()
1010         { return parent; }
1012     std::vector<Element *> getChildren()
1013         { return children; }
1015     std::vector<Element *> findElements(const String &name);
1017     String getAttribute(const String &name);
1019     std::vector<Attribute> &getAttributes()
1020         { return attributes; } 
1022     String getTagAttribute(const String &tagName, const String &attrName);
1024     String getTagValue(const String &tagName);
1026     void addChild(Element *child);
1028     void addAttribute(const String &name, const String &value);
1030     void addNamespace(const String &prefix, const String &namespaceURI);
1033     /**
1034      * Prettyprint an XML tree to an output stream.  Elements are indented
1035      * according to element hierarchy.
1036      * @param f a stream to receive the output
1037      * @param elem the element to output
1038      */
1039     void writeIndented(FILE *f);
1041     /**
1042      * Prettyprint an XML tree to standard output.  This is the equivalent of
1043      * writeIndented(stdout).
1044      * @param elem the element to output
1045      */
1046     void print();
1047     
1048     int getLine()
1049         { return line; }
1051 protected:
1053     void init()
1054         {
1055         parent = NULL;
1056         line   = 0;
1057         }
1059     void assign(const Element &other)
1060         {
1061         parent     = other.parent;
1062         children   = other.children;
1063         attributes = other.attributes;
1064         namespaces = other.namespaces;
1065         name       = other.name;
1066         value      = other.value;
1067         line       = other.line;
1068         }
1070     void findElementsRecursive(std::vector<Element *>&res, const String &name);
1072     void writeIndentedRecursive(FILE *f, int indent);
1074     Element *parent;
1076     std::vector<Element *>children;
1078     std::vector<Attribute> attributes;
1079     std::vector<Namespace> namespaces;
1081     String name;
1082     String value;
1083     
1084     int line;
1085 };
1091 class Parser
1093 public:
1094     /**
1095      * Constructor
1096      */
1097     Parser()
1098         { init(); }
1100     virtual ~Parser()
1101         {}
1103     /**
1104      * Parse XML in a char buffer.
1105      * @param buf a character buffer to parse
1106      * @param pos position to start parsing
1107      * @param len number of chars, from pos, to parse.
1108      * @return a pointer to the root of the XML document;
1109      */
1110     Element *parse(const char *buf,int pos,int len);
1112     /**
1113      * Parse XML in a char buffer.
1114      * @param buf a character buffer to parse
1115      * @param pos position to start parsing
1116      * @param len number of chars, from pos, to parse.
1117      * @return a pointer to the root of the XML document;
1118      */
1119     Element *parse(const String &buf);
1121     /**
1122      * Parse a named XML file.  The file is loaded like a data file;
1123      * the original format is not preserved.
1124      * @param fileName the name of the file to read
1125      * @return a pointer to the root of the XML document;
1126      */
1127     Element *parseFile(const String &fileName);
1129     /**
1130      * Utility method to preprocess a string for XML
1131      * output, escaping its entities.
1132      * @param str the string to encode
1133      */
1134     static String encode(const String &str);
1136     /**
1137      *  Removes whitespace from beginning and end of a string
1138      */
1139     String trim(const String &s);
1141 private:
1143     void init()
1144         {
1145         keepGoing       = true;
1146         currentNode     = NULL;
1147         parselen        = 0;
1148         parsebuf        = NULL;
1149         currentPosition = 0;
1150         }
1152     int countLines(int begin, int end);
1154     void getLineAndColumn(int pos, int *lineNr, int *colNr);
1156     void error(const char *fmt, ...);
1158     int peek(int pos);
1160     int match(int pos, const char *text);
1162     int skipwhite(int p);
1164     int getWord(int p0, String &buf);
1166     int getQuoted(int p0, String &buf, int do_i_parse);
1168     int parseVersion(int p0);
1170     int parseDoctype(int p0);
1172     int parseElement(int p0, Element *par,int depth);
1174     Element *parse(XMLCh *buf,int pos,int len);
1176     bool       keepGoing;
1177     Element    *currentNode;
1178     int        parselen;
1179     XMLCh      *parsebuf;
1180     String     cdatabuf;
1181     int        currentPosition;
1182 };
1187 //########################################################################
1188 //# E L E M E N T
1189 //########################################################################
1191 Element *Element::clone()
1193     Element *elem = new Element(name, value);
1194     elem->parent     = parent;
1195     elem->attributes = attributes;
1196     elem->namespaces = namespaces;
1197     elem->line       = line;
1199     std::vector<Element *>::iterator iter;
1200     for (iter = children.begin(); iter != children.end() ; iter++)
1201         {
1202         elem->addChild((*iter)->clone());
1203         }
1204     return elem;
1208 void Element::findElementsRecursive(std::vector<Element *>&res, const String &name)
1210     if (getName() == name)
1211         {
1212         res.push_back(this);
1213         }
1214     for (unsigned int i=0; i<children.size() ; i++)
1215         children[i]->findElementsRecursive(res, name);
1218 std::vector<Element *> Element::findElements(const String &name)
1220     std::vector<Element *> res;
1221     findElementsRecursive(res, name);
1222     return res;
1225 String Element::getAttribute(const String &name)
1227     for (unsigned int i=0 ; i<attributes.size() ; i++)
1228         if (attributes[i].getName() ==name)
1229             return attributes[i].getValue();
1230     return "";
1233 String Element::getTagAttribute(const String &tagName, const String &attrName)
1235     std::vector<Element *>elems = findElements(tagName);
1236     if (elems.size() <1)
1237         return "";
1238     String res = elems[0]->getAttribute(attrName);
1239     return res;
1242 String Element::getTagValue(const String &tagName)
1244     std::vector<Element *>elems = findElements(tagName);
1245     if (elems.size() <1)
1246         return "";
1247     String res = elems[0]->getValue();
1248     return res;
1251 void Element::addChild(Element *child)
1253     if (!child)
1254         return;
1255     child->parent = this;
1256     children.push_back(child);
1260 void Element::addAttribute(const String &name, const String &value)
1262     Attribute attr(name, value);
1263     attributes.push_back(attr);
1266 void Element::addNamespace(const String &prefix, const String &namespaceURI)
1268     Namespace ns(prefix, namespaceURI);
1269     namespaces.push_back(ns);
1272 void Element::writeIndentedRecursive(FILE *f, int indent)
1274     int i;
1275     if (!f)
1276         return;
1277     //Opening tag, and attributes
1278     for (i=0;i<indent;i++)
1279         fputc(' ',f);
1280     fprintf(f,"<%s",name.c_str());
1281     for (unsigned int i=0 ; i<attributes.size() ; i++)
1282         {
1283         fprintf(f," %s=\"%s\"",
1284               attributes[i].getName().c_str(),
1285               attributes[i].getValue().c_str());
1286         }
1287     for (unsigned int i=0 ; i<namespaces.size() ; i++)
1288         {
1289         fprintf(f," xmlns:%s=\"%s\"",
1290               namespaces[i].getPrefix().c_str(),
1291               namespaces[i].getNamespaceURI().c_str());
1292         }
1293     fprintf(f,">\n");
1295     //Between the tags
1296     if (value.size() > 0)
1297         {
1298         for (int i=0;i<indent;i++)
1299             fputc(' ', f);
1300         fprintf(f," %s\n", value.c_str());
1301         }
1303     for (unsigned int i=0 ; i<children.size() ; i++)
1304         children[i]->writeIndentedRecursive(f, indent+2);
1306     //Closing tag
1307     for (int i=0; i<indent; i++)
1308         fputc(' ',f);
1309     fprintf(f,"</%s>\n", name.c_str());
1312 void Element::writeIndented(FILE *f)
1314     writeIndentedRecursive(f, 0);
1317 void Element::print()
1319     writeIndented(stdout);
1323 //########################################################################
1324 //# P A R S E R
1325 //########################################################################
1329 typedef struct
1330     {
1331     const char *escaped;
1332     char value;
1333     } EntityEntry;
1335 static EntityEntry entities[] =
1337     { "&amp;" , '&'  },
1338     { "&lt;"  , '<'  },
1339     { "&gt;"  , '>'  },
1340     { "&apos;", '\'' },
1341     { "&quot;", '"'  },
1342     { NULL    , '\0' }
1343 };
1347 /**
1348  *  Removes whitespace from beginning and end of a string
1349  */
1350 String Parser::trim(const String &s)
1352     if (s.size() < 1)
1353         return s;
1354     
1355     //Find first non-ws char
1356     unsigned int begin = 0;
1357     for ( ; begin < s.size() ; begin++)
1358         {
1359         if (!isspace(s[begin]))
1360             break;
1361         }
1363     //Find first non-ws char, going in reverse
1364     unsigned int end = s.size() - 1;
1365     for ( ; end > begin ; end--)
1366         {
1367         if (!isspace(s[end]))
1368             break;
1369         }
1370     //trace("begin:%d  end:%d", begin, end);
1372     String res = s.substr(begin, end-begin+1);
1373     return res;
1377 int Parser::countLines(int begin, int end)
1379     int count = 0;
1380     for (int i=begin ; i<end ; i++)
1381         {
1382         XMLCh ch = parsebuf[i];
1383         if (ch == '\n' || ch == '\r')
1384             count++;
1385         }
1386     return count;
1390 void Parser::getLineAndColumn(int pos, int *lineNr, int *colNr)
1392     int line = 1;
1393     int col  = 1;
1394     for (long i=0 ; i<pos ; i++)
1395         {
1396         XMLCh ch = parsebuf[i];
1397         if (ch == '\n' || ch == '\r')
1398             {
1399             col = 0;
1400             line ++;
1401             }
1402         else
1403             col++;
1404         }
1405     *lineNr = line;
1406     *colNr  = col;
1411 void Parser::error(const char *fmt, ...)
1413     int lineNr;
1414     int colNr;
1415     getLineAndColumn(currentPosition, &lineNr, &colNr);
1416     va_list args;
1417     fprintf(stderr, "xml error at line %d, column %d:", lineNr, colNr);
1418     va_start(args,fmt);
1419     vfprintf(stderr,fmt,args);
1420     va_end(args) ;
1421     fprintf(stderr, "\n");
1426 int Parser::peek(int pos)
1428     if (pos >= parselen)
1429         return -1;
1430     currentPosition = pos;
1431     int ch = parsebuf[pos];
1432     //printf("ch:%c\n", ch);
1433     return ch;
1438 String Parser::encode(const String &str)
1440     String ret;
1441     for (unsigned int i=0 ; i<str.size() ; i++)
1442         {
1443         XMLCh ch = (XMLCh)str[i];
1444         if (ch == '&')
1445             ret.append("&amp;");
1446         else if (ch == '<')
1447             ret.append("&lt;");
1448         else if (ch == '>')
1449             ret.append("&gt;");
1450         else if (ch == '\'')
1451             ret.append("&apos;");
1452         else if (ch == '"')
1453             ret.append("&quot;");
1454         else
1455             ret.push_back(ch);
1457         }
1458     return ret;
1462 int Parser::match(int p0, const char *text)
1464     int p = p0;
1465     while (*text)
1466         {
1467         if (peek(p) != *text)
1468             return p0;
1469         p++; text++;
1470         }
1471     return p;
1476 int Parser::skipwhite(int p)
1479     while (p<parselen)
1480         {
1481         int p2 = match(p, "<!--");
1482         if (p2 > p)
1483             {
1484             p = p2;
1485             while (p<parselen)
1486               {
1487               p2 = match(p, "-->");
1488               if (p2 > p)
1489                   {
1490                   p = p2;
1491                   break;
1492                   }
1493               p++;
1494               }
1495           }
1496       XMLCh b = peek(p);
1497       if (!isspace(b))
1498           break;
1499       p++;
1500       }
1501   return p;
1504 /* modify this to allow all chars for an element or attribute name*/
1505 int Parser::getWord(int p0, String &buf)
1507     int p = p0;
1508     while (p<parselen)
1509         {
1510         XMLCh b = peek(p);
1511         if (b<=' ' || b=='/' || b=='>' || b=='=')
1512             break;
1513         buf.push_back(b);
1514         p++;
1515         }
1516     return p;
1519 int Parser::getQuoted(int p0, String &buf, int do_i_parse)
1522     int p = p0;
1523     if (peek(p) != '"' && peek(p) != '\'')
1524         return p0;
1525     p++;
1527     while ( p<parselen )
1528         {
1529         XMLCh b = peek(p);
1530         if (b=='"' || b=='\'')
1531             break;
1532         if (b=='&' && do_i_parse)
1533             {
1534             bool found = false;
1535             for (EntityEntry *ee = entities ; ee->value ; ee++)
1536                 {
1537                 int p2 = match(p, ee->escaped);
1538                 if (p2>p)
1539                     {
1540                     buf.push_back(ee->value);
1541                     p = p2;
1542                     found = true;
1543                     break;
1544                     }
1545                 }
1546             if (!found)
1547                 {
1548                 error("unterminated entity");
1549                 return false;
1550                 }
1551             }
1552         else
1553             {
1554             buf.push_back(b);
1555             p++;
1556             }
1557         }
1558     return p;
1561 int Parser::parseVersion(int p0)
1563     //printf("### parseVersion: %d\n", p0);
1565     int p = p0;
1567     p = skipwhite(p0);
1569     if (peek(p) != '<')
1570         return p0;
1572     p++;
1573     if (p>=parselen || peek(p)!='?')
1574         return p0;
1576     p++;
1578     String buf;
1580     while (p<parselen)
1581         {
1582         XMLCh ch = peek(p);
1583         if (ch=='?')
1584             {
1585             p++;
1586             break;
1587             }
1588         buf.push_back(ch);
1589         p++;
1590         }
1592     if (peek(p) != '>')
1593         return p0;
1594     p++;
1596     //printf("Got version:%s\n",buf.c_str());
1597     return p;
1600 int Parser::parseDoctype(int p0)
1602     //printf("### parseDoctype: %d\n", p0);
1604     int p = p0;
1605     p = skipwhite(p);
1607     if (p>=parselen || peek(p)!='<')
1608         return p0;
1610     p++;
1612     if (peek(p)!='!' || peek(p+1)=='-')
1613         return p0;
1614     p++;
1616     String buf;
1617     while (p<parselen)
1618         {
1619         XMLCh ch = peek(p);
1620         if (ch=='>')
1621             {
1622             p++;
1623             break;
1624             }
1625         buf.push_back(ch);
1626         p++;
1627         }
1629     //printf("Got doctype:%s\n",buf.c_str());
1630     return p;
1635 int Parser::parseElement(int p0, Element *par,int lineNr)
1638     int p = p0;
1640     int p2 = p;
1642     p = skipwhite(p);
1644     //## Get open tag
1645     XMLCh ch = peek(p);
1646     if (ch!='<')
1647         return p0;
1649     //int line, col;
1650     //getLineAndColumn(p, &line, &col);
1652     p++;
1654     String openTagName;
1655     p = skipwhite(p);
1656     p = getWord(p, openTagName);
1657     //printf("####tag :%s\n", openTagName.c_str());
1658     p = skipwhite(p);
1660     //Add element to tree
1661     Element *n = new Element(openTagName);
1662     n->line = lineNr + countLines(p0, p);
1663     n->parent = par;
1664     par->addChild(n);
1666     // Get attributes
1667     if (peek(p) != '>')
1668         {
1669         while (p<parselen)
1670             {
1671             p = skipwhite(p);
1672             ch = peek(p);
1673             //printf("ch:%c\n",ch);
1674             if (ch=='>')
1675                 break;
1676             else if (ch=='/' && p<parselen+1)
1677                 {
1678                 p++;
1679                 p = skipwhite(p);
1680                 ch = peek(p);
1681                 if (ch=='>')
1682                     {
1683                     p++;
1684                     //printf("quick close\n");
1685                     return p;
1686                     }
1687                 }
1688             String attrName;
1689             p2 = getWord(p, attrName);
1690             if (p2==p)
1691                 break;
1692             //printf("name:%s",buf);
1693             p=p2;
1694             p = skipwhite(p);
1695             ch = peek(p);
1696             //printf("ch:%c\n",ch);
1697             if (ch!='=')
1698                 break;
1699             p++;
1700             p = skipwhite(p);
1701             // ch = parsebuf[p];
1702             // printf("ch:%c\n",ch);
1703             String attrVal;
1704             p2 = getQuoted(p, attrVal, true);
1705             p=p2+1;
1706             //printf("name:'%s'   value:'%s'\n",attrName.c_str(),attrVal.c_str());
1707             char *namestr = (char *)attrName.c_str();
1708             if (strncmp(namestr, "xmlns:", 6)==0)
1709                 n->addNamespace(attrName, attrVal);
1710             else
1711                 n->addAttribute(attrName, attrVal);
1712             }
1713         }
1715     bool cdata = false;
1717     p++;
1718     // ### Get intervening data ### */
1719     String data;
1720     while (p<parselen)
1721         {
1722         //# COMMENT
1723         p2 = match(p, "<!--");
1724         if (!cdata && p2>p)
1725             {
1726             p = p2;
1727             while (p<parselen)
1728                 {
1729                 p2 = match(p, "-->");
1730                 if (p2 > p)
1731                     {
1732                     p = p2;
1733                     break;
1734                     }
1735                 p++;
1736                 }
1737             }
1739         ch = peek(p);
1740         //# END TAG
1741         if (ch=='<' && !cdata && peek(p+1)=='/')
1742             {
1743             break;
1744             }
1745         //# CDATA
1746         p2 = match(p, "<![CDATA[");
1747         if (p2 > p)
1748             {
1749             cdata = true;
1750             p = p2;
1751             continue;
1752             }
1754         //# CHILD ELEMENT
1755         if (ch == '<')
1756             {
1757             p2 = parseElement(p, n, lineNr + countLines(p0, p));
1758             if (p2 == p)
1759                 {
1760                 /*
1761                 printf("problem on element:%s.  p2:%d p:%d\n",
1762                       openTagName.c_str(), p2, p);
1763                 */
1764                 return p0;
1765                 }
1766             p = p2;
1767             continue;
1768             }
1769         //# ENTITY
1770         if (ch=='&' && !cdata)
1771             {
1772             bool found = false;
1773             for (EntityEntry *ee = entities ; ee->value ; ee++)
1774                 {
1775                 int p2 = match(p, ee->escaped);
1776                 if (p2>p)
1777                     {
1778                     data.push_back(ee->value);
1779                     p = p2;
1780                     found = true;
1781                     break;
1782                     }
1783                 }
1784             if (!found)
1785                 {
1786                 error("unterminated entity");
1787                 return -1;
1788                 }
1789             continue;
1790             }
1792         //# NONE OF THE ABOVE
1793         data.push_back(ch);
1794         p++;
1795         }/*while*/
1798     n->value = data;
1799     //printf("%d : data:%s\n",p,data.c_str());
1801     //## Get close tag
1802     p = skipwhite(p);
1803     ch = peek(p);
1804     if (ch != '<')
1805         {
1806         error("no < for end tag\n");
1807         return p0;
1808         }
1809     p++;
1810     ch = peek(p);
1811     if (ch != '/')
1812         {
1813         error("no / on end tag");
1814         return p0;
1815         }
1816     p++;
1817     ch = peek(p);
1818     p = skipwhite(p);
1819     String closeTagName;
1820     p = getWord(p, closeTagName);
1821     if (openTagName != closeTagName)
1822         {
1823         error("Mismatched closing tag.  Expected </%S>. Got '%S'.",
1824                 openTagName.c_str(), closeTagName.c_str());
1825         return p0;
1826         }
1827     p = skipwhite(p);
1828     if (peek(p) != '>')
1829         {
1830         error("no > on end tag for '%s'", closeTagName.c_str());
1831         return p0;
1832         }
1833     p++;
1834     // printf("close element:%s\n",closeTagName.c_str());
1835     p = skipwhite(p);
1836     return p;
1842 Element *Parser::parse(XMLCh *buf,int pos,int len)
1844     parselen = len;
1845     parsebuf = buf;
1846     Element *rootNode = new Element("root");
1847     pos = parseVersion(pos);
1848     pos = parseDoctype(pos);
1849     pos = parseElement(pos, rootNode, 1);
1850     return rootNode;
1854 Element *Parser::parse(const char *buf, int pos, int len)
1856     XMLCh *charbuf = new XMLCh[len + 1];
1857     long i = 0;
1858     for ( ; i < len ; i++)
1859         charbuf[i] = (XMLCh)buf[i];
1860     charbuf[i] = '\0';
1862     Element *n = parse(charbuf, pos, len);
1863     delete[] charbuf;
1864     return n;
1867 Element *Parser::parse(const String &buf)
1869     long len = (long)buf.size();
1870     XMLCh *charbuf = new XMLCh[len + 1];
1871     long i = 0;
1872     for ( ; i < len ; i++)
1873         charbuf[i] = (XMLCh)buf[i];
1874     charbuf[i] = '\0';
1876     Element *n = parse(charbuf, 0, len);
1877     delete[] charbuf;
1878     return n;
1881 Element *Parser::parseFile(const String &fileName)
1884     //##### LOAD INTO A CHAR BUF, THEN CONVERT TO XMLCh
1885     FILE *f = fopen(fileName.c_str(), "rb");
1886     if (!f)
1887         return NULL;
1889     struct stat  statBuf;
1890     if (fstat(fileno(f),&statBuf)<0)
1891         {
1892         fclose(f);
1893         return NULL;
1894         }
1895     long filelen = statBuf.st_size;
1897     //printf("length:%d\n",filelen);
1898     XMLCh *charbuf = new XMLCh[filelen + 1];
1899     for (XMLCh *p=charbuf ; !feof(f) ; p++)
1900         {
1901         *p = (XMLCh)fgetc(f);
1902         }
1903     fclose(f);
1904     charbuf[filelen] = '\0';
1907     /*
1908     printf("nrbytes:%d\n",wc_count);
1909     printf("buf:%ls\n======\n",charbuf);
1910     */
1911     Element *n = parse(charbuf, 0, filelen);
1912     delete[] charbuf;
1913     return n;
1916 //########################################################################
1917 //########################################################################
1918 //##  E N D    X M L
1919 //########################################################################
1920 //########################################################################
1927 //########################################################################
1928 //########################################################################
1929 //##  U R I
1930 //########################################################################
1931 //########################################################################
1933 //This would normally be a call to a UNICODE function
1934 #define isLetter(x) isalpha(x)
1936 /**
1937  *  A class that implements the W3C URI resource reference.
1938  */
1939 class URI
1941 public:
1943     typedef enum
1944         {
1945         SCHEME_NONE =0,
1946         SCHEME_DATA,
1947         SCHEME_HTTP,
1948         SCHEME_HTTPS,
1949         SCHEME_FTP,
1950         SCHEME_FILE,
1951         SCHEME_LDAP,
1952         SCHEME_MAILTO,
1953         SCHEME_NEWS,
1954         SCHEME_TELNET
1955         } SchemeTypes;
1957     /**
1958      *
1959      */
1960     URI()
1961         {
1962         init();
1963         }
1965     /**
1966      *
1967      */
1968     URI(const String &str)
1969         {
1970         init();
1971         parse(str);
1972         }
1975     /**
1976      *
1977      */
1978     URI(const char *str)
1979         {
1980         init();
1981         String domStr = str;
1982         parse(domStr);
1983         }
1986     /**
1987      *
1988      */
1989     URI(const URI &other)
1990         {
1991         init();
1992         assign(other);
1993         }
1996     /**
1997      *
1998      */
1999     URI &operator=(const URI &other)
2000         {
2001         init();
2002         assign(other);
2003         return *this;
2004         }
2007     /**
2008      *
2009      */
2010     virtual ~URI()
2011         {}
2015     /**
2016      *
2017      */
2018     virtual bool parse(const String &str);
2020     /**
2021      *
2022      */
2023     virtual String toString() const;
2025     /**
2026      *
2027      */
2028     virtual int getScheme() const;
2030     /**
2031      *
2032      */
2033     virtual String getSchemeStr() const;
2035     /**
2036      *
2037      */
2038     virtual String getAuthority() const;
2040     /**
2041      *  Same as getAuthority, but if the port has been specified
2042      *  as host:port , the port will not be included
2043      */
2044     virtual String getHost() const;
2046     /**
2047      *
2048      */
2049     virtual int getPort() const;
2051     /**
2052      *
2053      */
2054     virtual String getPath() const;
2056     /**
2057      *
2058      */
2059     virtual String getNativePath() const;
2061     /**
2062      *
2063      */
2064     virtual bool isAbsolute() const;
2066     /**
2067      *
2068      */
2069     virtual bool isOpaque() const;
2071     /**
2072      *
2073      */
2074     virtual String getQuery() const;
2076     /**
2077      *
2078      */
2079     virtual String getFragment() const;
2081     /**
2082      *
2083      */
2084     virtual URI resolve(const URI &other) const;
2086     /**
2087      *
2088      */
2089     virtual void normalize();
2091 private:
2093     /**
2094      *
2095      */
2096     void init()
2097         {
2098         parsebuf  = NULL;
2099         parselen  = 0;
2100         scheme    = SCHEME_NONE;
2101         schemeStr = "";
2102         port      = 0;
2103         authority = "";
2104         path      = "";
2105         absolute  = false;
2106         opaque    = false;
2107         query     = "";
2108         fragment  = "";
2109         }
2112     /**
2113      *
2114      */
2115     void assign(const URI &other)
2116         {
2117         scheme    = other.scheme;
2118         schemeStr = other.schemeStr;
2119         authority = other.authority;
2120         port      = other.port;
2121         path      = other.path;
2122         absolute  = other.absolute;
2123         opaque    = other.opaque;
2124         query     = other.query;
2125         fragment  = other.fragment;
2126         }
2128     int scheme;
2130     String schemeStr;
2132     String authority;
2134     bool portSpecified;
2136     int port;
2138     String path;
2140     bool absolute;
2142     bool opaque;
2144     String query;
2146     String fragment;
2148     void error(const char *fmt, ...);
2150     void trace(const char *fmt, ...);
2153     int peek(int p);
2155     int match(int p, const char *key);
2157     int parseScheme(int p);
2159     int parseHierarchicalPart(int p0);
2161     int parseQuery(int p0);
2163     int parseFragment(int p0);
2165     int parse(int p);
2167     char *parsebuf;
2169     int parselen;
2171 };
2175 typedef struct
2177     int         ival;
2178     const char *sval;
2179     int         port;
2180 } LookupEntry;
2182 LookupEntry schemes[] =
2184     { URI::SCHEME_DATA,   "data:",    0 },
2185     { URI::SCHEME_HTTP,   "http:",   80 },
2186     { URI::SCHEME_HTTPS,  "https:", 443 },
2187     { URI::SCHEME_FTP,    "ftp",     12 },
2188     { URI::SCHEME_FILE,   "file:",    0 },
2189     { URI::SCHEME_LDAP,   "ldap:",  123 },
2190     { URI::SCHEME_MAILTO, "mailto:", 25 },
2191     { URI::SCHEME_NEWS,   "news:",  117 },
2192     { URI::SCHEME_TELNET, "telnet:", 23 },
2193     { 0,                  NULL,       0 }
2194 };
2197 String URI::toString() const
2199     String str = schemeStr;
2200     if (authority.size() > 0)
2201         {
2202         str.append("//");
2203         str.append(authority);
2204         }
2205     str.append(path);
2206     if (query.size() > 0)
2207         {
2208         str.append("?");
2209         str.append(query);
2210         }
2211     if (fragment.size() > 0)
2212         {
2213         str.append("#");
2214         str.append(fragment);
2215         }
2216     return str;
2220 int URI::getScheme() const
2222     return scheme;
2225 String URI::getSchemeStr() const
2227     return schemeStr;
2231 String URI::getAuthority() const
2233     String ret = authority;
2234     if (portSpecified && port>=0)
2235         {
2236         char buf[7];
2237         snprintf(buf, 6, ":%6d", port);
2238         ret.append(buf);
2239         }
2240     return ret;
2243 String URI::getHost() const
2245     return authority;
2248 int URI::getPort() const
2250     return port;
2254 String URI::getPath() const
2256     return path;
2259 String URI::getNativePath() const
2261     String npath;
2262 #ifdef __WIN32__
2263     unsigned int firstChar = 0;
2264     if (path.size() >= 3)
2265         {
2266         if (path[0] == '/' &&
2267             isLetter(path[1]) &&
2268             path[2] == ':')
2269             firstChar++;
2270          }
2271     for (unsigned int i=firstChar ; i<path.size() ; i++)
2272         {
2273         XMLCh ch = (XMLCh) path[i];
2274         if (ch == '/')
2275             npath.push_back((XMLCh)'\\');
2276         else
2277             npath.push_back(ch);
2278         }
2279 #else
2280     npath = path;
2281 #endif
2282     return npath;
2286 bool URI::isAbsolute() const
2288     return absolute;
2291 bool URI::isOpaque() const
2293     return opaque;
2297 String URI::getQuery() const
2299     return query;
2303 String URI::getFragment() const
2305     return fragment;
2309 URI URI::resolve(const URI &other) const
2311     //### According to w3c, this is handled in 3 cases
2313     //## 1
2314     if (opaque || other.isAbsolute())
2315         return other;
2317     //## 2
2318     if (other.fragment.size()  >  0 &&
2319         other.path.size()      == 0 &&
2320         other.scheme           == SCHEME_NONE &&
2321         other.authority.size() == 0 &&
2322         other.query.size()     == 0 )
2323         {
2324         URI fragUri = *this;
2325         fragUri.fragment = other.fragment;
2326         return fragUri;
2327         }
2329     //## 3 http://www.ietf.org/rfc/rfc2396.txt, section 5.2
2330     URI newUri;
2331     //# 3.1
2332     newUri.scheme    = scheme;
2333     newUri.schemeStr = schemeStr;
2334     newUri.query     = other.query;
2335     newUri.fragment  = other.fragment;
2336     if (other.authority.size() > 0)
2337         {
2338         //# 3.2
2339         if (absolute || other.absolute)
2340             newUri.absolute = true;
2341         newUri.authority = other.authority;
2342         newUri.port      = other.port;//part of authority
2343         newUri.path      = other.path;
2344         }
2345     else
2346         {
2347         //# 3.3
2348         if (other.absolute)
2349             {
2350             newUri.absolute = true;
2351             newUri.path     = other.path;
2352             }
2353         else
2354             {
2355             unsigned int pos = path.find_last_of('/');
2356             if (pos != path.npos)
2357                 {
2358                 String tpath = path.substr(0, pos+1);
2359                 tpath.append(other.path);
2360                 newUri.path = tpath;
2361                 }
2362             else
2363                 newUri.path = other.path;
2364             }
2365         }
2367     newUri.normalize();
2368     return newUri;
2373 /**
2374  *  This follows the Java URI algorithm:
2375  *   1. All "." segments are removed.
2376  *   2. If a ".." segment is preceded by a non-".." segment
2377  *          then both of these segments are removed. This step
2378  *          is repeated until it is no longer applicable.
2379  *   3. If the path is relative, and if its first segment
2380  *          contains a colon character (':'), then a "." segment
2381  *          is prepended. This prevents a relative URI with a path
2382  *          such as "a:b/c/d" from later being re-parsed as an
2383  *          opaque URI with a scheme of "a" and a scheme-specific
2384  *          part of "b/c/d". (Deviation from RFC 2396)
2385  */
2386 void URI::normalize()
2388     std::vector<String> segments;
2390     //## Collect segments
2391     if (path.size()<2)
2392         return;
2393     bool abs = false;
2394     unsigned int pos=0;
2395     if (path[0]=='/')
2396         {
2397         abs = true;
2398         pos++;
2399         }
2400     while (pos < path.size())
2401         {
2402         unsigned int pos2 = path.find('/', pos);
2403         if (pos2==path.npos)
2404             {
2405             String seg = path.substr(pos);
2406             //printf("last segment:%s\n", seg.c_str());
2407             segments.push_back(seg);
2408             break;
2409             }
2410         if (pos2>pos)
2411             {
2412             String seg = path.substr(pos, pos2-pos);
2413             //printf("segment:%s\n", seg.c_str());
2414             segments.push_back(seg);
2415             }
2416         pos = pos2;
2417         pos++;
2418         }
2420     //## Clean up (normalize) segments
2421     bool edited = false;
2422     std::vector<String>::iterator iter;
2423     for (iter=segments.begin() ; iter!=segments.end() ; )
2424         {
2425         String s = *iter;
2426         if (s == ".")
2427             {
2428             iter = segments.erase(iter);
2429             edited = true;
2430             }
2431         else if (s == ".." &&
2432                  iter != segments.begin() &&
2433                  *(iter-1) != "..")
2434             {
2435             iter--; //back up, then erase two entries
2436             iter = segments.erase(iter);
2437             iter = segments.erase(iter);
2438             edited = true;
2439             }
2440         else
2441             iter++;
2442         }
2444     //## Rebuild path, if necessary
2445     if (edited)
2446         {
2447         path.clear();
2448         if (abs)
2449             {
2450             path.append("/");
2451             }
2452         std::vector<String>::iterator iter;
2453         for (iter=segments.begin() ; iter!=segments.end() ; iter++)
2454             {
2455             if (iter != segments.begin())
2456                 path.append("/");
2457             path.append(*iter);
2458             }
2459         }
2465 //#########################################################################
2466 //# M E S S A G E S
2467 //#########################################################################
2469 void URI::error(const char *fmt, ...)
2471     va_list args;
2472     fprintf(stderr, "URI error: ");
2473     va_start(args, fmt);
2474     vfprintf(stderr, fmt, args);
2475     va_end(args);
2476     fprintf(stderr, "\n");
2479 void URI::trace(const char *fmt, ...)
2481     va_list args;
2482     fprintf(stdout, "URI: ");
2483     va_start(args, fmt);
2484     vfprintf(stdout, fmt, args);
2485     va_end(args);
2486     fprintf(stdout, "\n");
2492 //#########################################################################
2493 //# P A R S I N G
2494 //#########################################################################
2498 int URI::peek(int p)
2500     if (p<0 || p>=parselen)
2501         return -1;
2502     return parsebuf[p];
2507 int URI::match(int p0, const char *key)
2509     int p = p0;
2510     while (p < parselen)
2511         {
2512         if (*key == '\0')
2513             return p;
2514         else if (*key != parsebuf[p])
2515             break;
2516         p++; key++;
2517         }
2518     return p0;
2521 //#########################################################################
2522 //#  Parsing is performed according to:
2523 //#  http://www.gbiv.com/protocols/uri/rfc/rfc3986.html#components
2524 //#########################################################################
2526 int URI::parseScheme(int p0)
2528     int p = p0;
2529     for (LookupEntry *entry = schemes; entry->sval ; entry++)
2530         {
2531         int p2 = match(p, entry->sval);
2532         if (p2 > p)
2533             {
2534             schemeStr = entry->sval;
2535             scheme    = entry->ival;
2536             port      = entry->port;
2537             p = p2;
2538             return p;
2539             }
2540         }
2542     return p;
2546 int URI::parseHierarchicalPart(int p0)
2548     int p = p0;
2549     int ch;
2551     //# Authority field (host and port, for example)
2552     int p2 = match(p, "//");
2553     if (p2 > p)
2554         {
2555         p = p2;
2556         portSpecified = false;
2557         String portStr;
2558         while (p < parselen)
2559             {
2560             ch = peek(p);
2561             if (ch == '/')
2562                 break;
2563             else if (ch == ':')
2564                 portSpecified = true;
2565             else if (portSpecified)
2566                 portStr.push_back((XMLCh)ch);
2567             else
2568                 authority.push_back((XMLCh)ch);
2569             p++;
2570             }
2571         if (portStr.size() > 0)
2572             {
2573             char *pstr = (char *)portStr.c_str();
2574             char *endStr;
2575             long val = strtol(pstr, &endStr, 10);
2576             if (endStr > pstr) //successful parse?
2577                 port = val;
2578             }
2579         }
2581     //# Are we absolute?
2582     ch = peek(p);
2583     if (isLetter(ch) && peek(p+1)==':')
2584         {
2585         absolute = true;
2586         path.push_back((XMLCh)'/');
2587         }
2588     else if (ch == '/')
2589         {
2590         absolute = true;
2591         if (p>p0) //in other words, if '/' is not the first char
2592             opaque = true;
2593         path.push_back((XMLCh)ch);
2594         p++;
2595         }
2597     while (p < parselen)
2598         {
2599         ch = peek(p);
2600         if (ch == '?' || ch == '#')
2601             break;
2602         path.push_back((XMLCh)ch);
2603         p++;
2604         }
2606     return p;
2609 int URI::parseQuery(int p0)
2611     int p = p0;
2612     int ch = peek(p);
2613     if (ch != '?')
2614         return p0;
2616     p++;
2617     while (p < parselen)
2618         {
2619         ch = peek(p);
2620         if (ch == '#')
2621             break;
2622         query.push_back((XMLCh)ch);
2623         p++;
2624         }
2627     return p;
2630 int URI::parseFragment(int p0)
2633     int p = p0;
2634     int ch = peek(p);
2635     if (ch != '#')
2636         return p0;
2638     p++;
2639     while (p < parselen)
2640         {
2641         ch = peek(p);
2642         if (ch == '?')
2643             break;
2644         fragment.push_back((XMLCh)ch);
2645         p++;
2646         }
2649     return p;
2653 int URI::parse(int p0)
2656     int p = p0;
2658     int p2 = parseScheme(p);
2659     if (p2 < 0)
2660         {
2661         error("Scheme");
2662         return -1;
2663         }
2664     p = p2;
2667     p2 = parseHierarchicalPart(p);
2668     if (p2 < 0)
2669         {
2670         error("Hierarchical part");
2671         return -1;
2672         }
2673     p = p2;
2675     p2 = parseQuery(p);
2676     if (p2 < 0)
2677         {
2678         error("Query");
2679         return -1;
2680         }
2681     p = p2;
2684     p2 = parseFragment(p);
2685     if (p2 < 0)
2686         {
2687         error("Fragment");
2688         return -1;
2689         }
2690     p = p2;
2692     return p;
2698 bool URI::parse(const String &str)
2700     init();
2701     
2702     parselen = str.size();
2704     String tmp;
2705     for (unsigned int i=0 ; i<str.size() ; i++)
2706         {
2707         XMLCh ch = (XMLCh) str[i];
2708         if (ch == '\\')
2709             tmp.push_back((XMLCh)'/');
2710         else
2711             tmp.push_back(ch);
2712         }
2713     parsebuf = (char *) tmp.c_str();
2716     int p = parse(0);
2717     normalize();
2719     if (p < 0)
2720         {
2721         error("Syntax error");
2722         return false;
2723         }
2725     //printf("uri:%s\n", toString().c_str());
2726     //printf("path:%s\n", path.c_str());
2728     return true;
2739 //########################################################################
2740 //########################################################################
2741 //##  M A K E
2742 //########################################################################
2743 //########################################################################
2745 //########################################################################
2746 //# Stat cache to speed up stat requests
2747 //########################################################################
2748 struct StatResult {
2749     int result;
2750     struct stat statInfo;
2751 };
2752 typedef std::map<String, StatResult> statCacheType;
2753 static statCacheType statCache;
2754 static int cachedStat(const String &f, struct stat *s) {
2755     //printf("Stat path: %s\n", f.c_str());
2756     std::pair<statCacheType::iterator, bool> result = statCache.insert(statCacheType::value_type(f, StatResult()));
2757     if (result.second) {
2758         result.first->second.result = stat(f.c_str(), &(result.first->second.statInfo));
2759     }
2760     *s = result.first->second.statInfo;
2761     return result.first->second.result;
2763 static void removeFromStatCache(const String f) {
2764     //printf("Removing from cache: %s\n", f.c_str());
2765     statCache.erase(f);
2768 //########################################################################
2769 //# 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);
5180 /**
5181  * Tests if the file exists
5182  */ 
5183 bool MakeBase::fileExists(const String &fileName)
5185     String native = getNativePath(fileName);
5186     struct stat finfo;
5187     
5188     //Exists?
5189     if (cachedStat(native, &finfo)<0)
5190         return false;
5192     return true;
5196 /**
5197  * Tests if the file exists and is a regular file
5198  */ 
5199 bool MakeBase::isRegularFile(const String &fileName)
5201     String native = getNativePath(fileName);
5202     struct stat finfo;
5203     
5204     //Exists?
5205     if (cachedStat(native, &finfo)<0)
5206         return false;
5209     //check the file mode
5210     if (!S_ISREG(finfo.st_mode))
5211         return false;
5213     return true;
5216 /**
5217  * Tests if the file exists and is a directory
5218  */ 
5219 bool MakeBase::isDirectory(const String &fileName)
5221     String native = getNativePath(fileName);
5222     struct stat finfo;
5223     
5224     //Exists?
5225     if (cachedStat(native, &finfo)<0)
5226         return false;
5229     //check the file mode
5230     if (!S_ISDIR(finfo.st_mode))
5231         return false;
5233     return true;
5238 /**
5239  * Tests is the modification of fileA is newer than fileB
5240  */ 
5241 bool MakeBase::isNewerThan(const String &fileA, const String &fileB)
5243     //trace("isNewerThan:'%s' , '%s'", fileA.c_str(), fileB.c_str());
5244     String nativeA = getNativePath(fileA);
5245     struct stat infoA;
5246     //IF source does not exist, NOT newer
5247     if (cachedStat(nativeA, &infoA)<0)
5248         {
5249         return false;
5250         }
5252     String nativeB = getNativePath(fileB);
5253     struct stat infoB;
5254     //IF dest does not exist, YES, newer
5255     if (cachedStat(nativeB, &infoB)<0)
5256         {
5257         return true;
5258         }
5260     //check the actual times
5261     if (infoA.st_mtime > infoB.st_mtime)
5262         {
5263         return true;
5264         }
5266     return false;
5270 //########################################################################
5271 //# P K G    C O N F I G
5272 //########################################################################
5275 /**
5276  * Get a character from the buffer at pos.  If out of range,
5277  * return -1 for safety
5278  */
5279 int PkgConfig::get(int pos)
5281     if (pos>parselen)
5282         return -1;
5283     return parsebuf[pos];
5288 /**
5289  *  Skip over all whitespace characters beginning at pos.  Return
5290  *  the position of the first non-whitespace character.
5291  *  Pkg-config is line-oriented, so check for newline
5292  */
5293 int PkgConfig::skipwhite(int pos)
5295     while (pos < parselen)
5296         {
5297         int ch = get(pos);
5298         if (ch < 0)
5299             break;
5300         if (!isspace(ch))
5301             break;
5302         pos++;
5303         }
5304     return pos;
5308 /**
5309  *  Parse the buffer beginning at pos, for a word.  Fill
5310  *  'ret' with the result.  Return the position after the
5311  *  word.
5312  */
5313 int PkgConfig::getword(int pos, String &ret)
5315     while (pos < parselen)
5316         {
5317         int ch = get(pos);
5318         if (ch < 0)
5319             break;
5320         if (!isalnum(ch) && ch != '_' && ch != '-' && ch != '+' && ch != '.')
5321             break;
5322         ret.push_back((char)ch);
5323         pos++;
5324         }
5325     return pos;
5328 bool PkgConfig::parseRequires()
5330     if (requires.size() == 0)
5331         return true;
5332     parsebuf = (char *)requires.c_str();
5333     parselen = requires.size();
5334     int pos = 0;
5335     while (pos < parselen)
5336         {
5337         pos = skipwhite(pos);
5338         String val;
5339         int pos2 = getword(pos, val);
5340         if (pos2 == pos)
5341             break;
5342         pos = pos2;
5343         //trace("val %s", val.c_str());
5344         requireList.push_back(val);
5345         }
5346     return true;
5350 static int getint(const String str)
5352     char *s = (char *)str.c_str();
5353     char *ends = NULL;
5354     long val = strtol(s, &ends, 10);
5355     if (ends == s)
5356         return 0L;
5357     else
5358         return val;
5361 void PkgConfig::parseVersion()
5363     if (version.size() == 0)
5364         return;
5365     String s1, s2, s3;
5366     unsigned int pos = 0;
5367     unsigned int pos2 = version.find('.', pos);
5368     if (pos2 == version.npos)
5369         {
5370         s1 = version;
5371         }
5372     else
5373         {
5374         s1 = version.substr(pos, pos2-pos);
5375         pos = pos2;
5376         pos++;
5377         if (pos < version.size())
5378             {
5379             pos2 = version.find('.', pos);
5380             if (pos2 == version.npos)
5381                 {
5382                 s2 = version.substr(pos, version.size()-pos);
5383                 }
5384             else
5385                 {
5386                 s2 = version.substr(pos, pos2-pos);
5387                 pos = pos2;
5388                 pos++;
5389                 if (pos < version.size())
5390                     s3 = version.substr(pos, pos2-pos);
5391                 }
5392             }
5393         }
5395     majorVersion = getint(s1);
5396     minorVersion = getint(s2);
5397     microVersion = getint(s3);
5398     //trace("version:%d.%d.%d", majorVersion,
5399     //          minorVersion, microVersion );
5403 bool PkgConfig::parseLine(const String &lineBuf)
5405     parsebuf = (char *)lineBuf.c_str();
5406     parselen = lineBuf.size();
5407     int pos = 0;
5408     
5409     while (pos < parselen)
5410         {
5411         String attrName;
5412         pos = skipwhite(pos);
5413         int ch = get(pos);
5414         if (ch == '#')
5415             {
5416             //comment.  eat the rest of the line
5417             while (pos < parselen)
5418                 {
5419                 ch = get(pos);
5420                 if (ch == '\n' || ch < 0)
5421                     break;
5422                 pos++;
5423                 }
5424             continue;
5425             }
5426         pos = getword(pos, attrName);
5427         if (attrName.size() == 0)
5428             continue;
5429         
5430         pos = skipwhite(pos);
5431         ch = get(pos);
5432         if (ch != ':' && ch != '=')
5433             {
5434             error("expected ':' or '='");
5435             return false;
5436             }
5437         pos++;
5438         pos = skipwhite(pos);
5439         String attrVal;
5440         while (pos < parselen)
5441             {
5442             ch = get(pos);
5443             if (ch == '\n' || ch < 0)
5444                 break;
5445             else if (ch == '$' && get(pos+1) == '{')
5446                 {
5447                 //#  this is a ${substitution}
5448                 pos += 2;
5449                 String subName;
5450                 while (pos < parselen)
5451                     {
5452                     ch = get(pos);
5453                     if (ch < 0)
5454                         {
5455                         error("unterminated substitution");
5456                         return false;
5457                         }
5458                     else if (ch == '}')
5459                         break;
5460                     else
5461                         subName.push_back((char)ch);
5462                     pos++;
5463                     }
5464                 //trace("subName:%s %s", subName.c_str(), prefix.c_str());
5465                 if (subName == "prefix" && prefix.size()>0)
5466                     {
5467                     attrVal.append(prefix);
5468                     //trace("prefix override:%s", prefix.c_str());
5469                     }
5470                 else
5471                     {
5472                     String subVal = attrs[subName];
5473                     //trace("subVal:%s", subVal.c_str());
5474                     attrVal.append(subVal);
5475                     }
5476                 }
5477             else
5478                 attrVal.push_back((char)ch);
5479             pos++;
5480             }
5482         attrVal = trim(attrVal);
5483         attrs[attrName] = attrVal;
5485         String attrNameL = toLower(attrName);
5487         if (attrNameL == "name")
5488             name = attrVal;
5489         else if (attrNameL == "description")
5490             description = attrVal;
5491         else if (attrNameL == "cflags")
5492             cflags = attrVal;
5493         else if (attrNameL == "libs")
5494             libs = attrVal;
5495         else if (attrNameL == "requires")
5496             requires = attrVal;
5497         else if (attrNameL == "version")
5498             version = attrVal;
5500         //trace("name:'%s'  value:'%s'",
5501         //      attrName.c_str(), attrVal.c_str());
5502         }
5504     return true;
5508 bool PkgConfig::parse(const String &buf)
5510     init();
5512     String line;
5513     int lineNr = 0;
5514     for (unsigned int p=0 ; p<buf.size() ; p++)
5515         {
5516         int ch = buf[p];
5517         if (ch == '\n' || ch == '\r')
5518             {
5519             if (!parseLine(line))
5520                 return false;
5521             line.clear();
5522             lineNr++;
5523             }
5524         else
5525             {
5526             line.push_back(ch);
5527             }
5528         }
5529     if (line.size()>0)
5530         {
5531         if (!parseLine(line))
5532             return false;
5533         }
5535     parseRequires();
5536     parseVersion();
5538     return true;
5544 void PkgConfig::dumpAttrs()
5546     //trace("### PkgConfig attributes for %s", fileName.c_str());
5547     std::map<String, String>::iterator iter;
5548     for (iter=attrs.begin() ; iter!=attrs.end() ; iter++)
5549         {
5550         trace("   %s = %s", iter->first.c_str(), iter->second.c_str());
5551         }
5555 bool PkgConfig::readFile(const String &fname)
5557     fileName = getNativePath(fname);
5559     FILE *f = fopen(fileName.c_str(), "r");
5560     if (!f)
5561         {
5562         error("cannot open file '%s' for reading", fileName.c_str());
5563         return false;
5564         }
5565     String buf;
5566     while (true)
5567         {
5568         int ch = fgetc(f);
5569         if (ch < 0)
5570             break;
5571         buf.push_back((char)ch);
5572         }
5573     fclose(f);
5575     //trace("####### File:\n%s", buf.c_str());
5576     if (!parse(buf))
5577         {
5578         return false;
5579         }
5581     //dumpAttrs();
5583     return true;
5588 bool PkgConfig::query(const String &pkgName)
5590     name = pkgName;
5592     String fname = path;
5593     fname.append("/");
5594     fname.append(name);
5595     fname.append(".pc");
5597     if (!readFile(fname))
5598         {
5599         error("Cannot find package '%s'. Do you have it installed?",
5600                        pkgName.c_str());
5601         return false;
5602         }
5603     
5604     return true;
5611 //########################################################################
5612 //# D E P T O O L
5613 //########################################################################
5617 /**
5618  *  Class which holds information for each file.
5619  */
5620 class FileRec
5622 public:
5624     typedef enum
5625         {
5626         UNKNOWN,
5627         CFILE,
5628         HFILE,
5629         OFILE
5630         } FileType;
5632     /**
5633      *  Constructor
5634      */
5635     FileRec()
5636         { init(); type = UNKNOWN; }
5638     /**
5639      *  Copy constructor
5640      */
5641     FileRec(const FileRec &other)
5642         { init(); assign(other); }
5643     /**
5644      *  Constructor
5645      */
5646     FileRec(int typeVal)
5647         { init(); type = typeVal; }
5648     /**
5649      *  Assignment operator
5650      */
5651     FileRec &operator=(const FileRec &other)
5652         { init(); assign(other); return *this; }
5655     /**
5656      *  Destructor
5657      */
5658     ~FileRec()
5659         {}
5661     /**
5662      *  Directory part of the file name
5663      */
5664     String path;
5666     /**
5667      *  Base name, sans directory and suffix
5668      */
5669     String baseName;
5671     /**
5672      *  File extension, such as cpp or h
5673      */
5674     String suffix;
5676     /**
5677      *  Type of file: CFILE, HFILE, OFILE
5678      */
5679     int type;
5681     /**
5682      * Used to list files ref'd by this one
5683      */
5684     std::map<String, FileRec *> files;
5687 private:
5689     void init()
5690         {
5691         }
5693     void assign(const FileRec &other)
5694         {
5695         type     = other.type;
5696         baseName = other.baseName;
5697         suffix   = other.suffix;
5698         files    = other.files;
5699         }
5701 };
5705 /**
5706  *  Simpler dependency record
5707  */
5708 class DepRec
5710 public:
5712     /**
5713      *  Constructor
5714      */
5715     DepRec()
5716         {init();}
5718     /**
5719      *  Copy constructor
5720      */
5721     DepRec(const DepRec &other)
5722         {init(); assign(other);}
5723     /**
5724      *  Constructor
5725      */
5726     DepRec(const String &fname)
5727         {init(); name = fname; }
5728     /**
5729      *  Assignment operator
5730      */
5731     DepRec &operator=(const DepRec &other)
5732         {init(); assign(other); return *this;}
5735     /**
5736      *  Destructor
5737      */
5738     ~DepRec()
5739         {}
5741     /**
5742      *  Directory part of the file name
5743      */
5744     String path;
5746     /**
5747      *  Base name, without the path and suffix
5748      */
5749     String name;
5751     /**
5752      *  Suffix of the source
5753      */
5754     String suffix;
5757     /**
5758      * Used to list files ref'd by this one
5759      */
5760     std::vector<String> files;
5763 private:
5765     void init()
5766         {
5767         }
5769     void assign(const DepRec &other)
5770         {
5771         path     = other.path;
5772         name     = other.name;
5773         suffix   = other.suffix;
5774         files    = other.files; //avoid recursion
5775         }
5777 };
5780 class DepTool : public MakeBase
5782 public:
5784     /**
5785      *  Constructor
5786      */
5787     DepTool()
5788         { init(); }
5790     /**
5791      *  Copy constructor
5792      */
5793     DepTool(const DepTool &other)
5794         { init(); assign(other); }
5796     /**
5797      *  Assignment operator
5798      */
5799     DepTool &operator=(const DepTool &other)
5800         { init(); assign(other); return *this; }
5803     /**
5804      *  Destructor
5805      */
5806     ~DepTool()
5807         {}
5810     /**
5811      *  Reset this section of code
5812      */
5813     virtual void init();
5814     
5815     /**
5816      *  Reset this section of code
5817      */
5818     virtual void assign(const DepTool &other)
5819         {
5820         }
5821     
5822     /**
5823      *  Sets the source directory which will be scanned
5824      */
5825     virtual void setSourceDirectory(const String &val)
5826         { sourceDir = val; }
5828     /**
5829      *  Returns the source directory which will be scanned
5830      */
5831     virtual String getSourceDirectory()
5832         { return sourceDir; }
5834     /**
5835      *  Sets the list of files within the directory to analyze
5836      */
5837     virtual void setFileList(const std::vector<String> &list)
5838         { fileList = list; }
5840     /**
5841      * Creates the list of all file names which will be
5842      * candidates for further processing.  Reads make.exclude
5843      * to see which files for directories to leave out.
5844      */
5845     virtual bool createFileList();
5848     /**
5849      *  Generates the forward dependency list
5850      */
5851     virtual bool generateDependencies();
5854     /**
5855      *  Generates the forward dependency list, saving the file
5856      */
5857     virtual bool generateDependencies(const String &);
5860     /**
5861      *  Load a dependency file
5862      */
5863     std::vector<DepRec> loadDepFile(const String &fileName);
5865     /**
5866      *  Load a dependency file, generating one if necessary
5867      */
5868     std::vector<DepRec> getDepFile(const String &fileName,
5869               bool forceRefresh);
5871     /**
5872      *  Save a dependency file
5873      */
5874     bool saveDepFile(const String &fileName);
5877 private:
5880     /**
5881      *
5882      */
5883     void parseName(const String &fullname,
5884                    String &path,
5885                    String &basename,
5886                    String &suffix);
5888     /**
5889      *
5890      */
5891     int get(int pos);
5893     /**
5894      *
5895      */
5896     int skipwhite(int pos);
5898     /**
5899      *
5900      */
5901     int getword(int pos, String &ret);
5903     /**
5904      *
5905      */
5906     bool sequ(int pos, const char *key);
5908     /**
5909      *
5910      */
5911     bool addIncludeFile(FileRec *frec, const String &fname);
5913     /**
5914      *
5915      */
5916     bool scanFile(const String &fname, FileRec *frec);
5918     /**
5919      *
5920      */
5921     bool processDependency(FileRec *ofile, FileRec *include);
5923     /**
5924      *
5925      */
5926     String sourceDir;
5928     /**
5929      *
5930      */
5931     std::vector<String> fileList;
5933     /**
5934      *
5935      */
5936     std::vector<String> directories;
5938     /**
5939      * A list of all files which will be processed for
5940      * dependencies.
5941      */
5942     std::map<String, FileRec *> allFiles;
5944     /**
5945      * The list of .o files, and the
5946      * dependencies upon them.
5947      */
5948     std::map<String, FileRec *> oFiles;
5950     int depFileSize;
5951     char *depFileBuf;
5953     static const int readBufSize = 8192;
5954     char readBuf[8193];//byte larger
5956 };
5962 /**
5963  *  Clean up after processing.  Called by the destructor, but should
5964  *  also be called before the object is reused.
5965  */
5966 void DepTool::init()
5968     sourceDir = ".";
5970     fileList.clear();
5971     directories.clear();
5972     
5973     //clear output file list
5974     std::map<String, FileRec *>::iterator iter;
5975     for (iter=oFiles.begin(); iter!=oFiles.end() ; iter++)
5976         delete iter->second;
5977     oFiles.clear();
5979     //allFiles actually contains the master copies. delete them
5980     for (iter= allFiles.begin(); iter!=allFiles.end() ; iter++)
5981         delete iter->second;
5982     allFiles.clear(); 
5989 /**
5990  *  Parse a full path name into path, base name, and suffix
5991  */
5992 void DepTool::parseName(const String &fullname,
5993                         String &path,
5994                         String &basename,
5995                         String &suffix)
5997     if (fullname.size() < 2)
5998         return;
6000     unsigned int pos = fullname.find_last_of('/');
6001     if (pos != fullname.npos && pos<fullname.size()-1)
6002         {
6003         path = fullname.substr(0, pos);
6004         pos++;
6005         basename = fullname.substr(pos, fullname.size()-pos);
6006         }
6007     else
6008         {
6009         path = "";
6010         basename = fullname;
6011         }
6013     pos = basename.find_last_of('.');
6014     if (pos != basename.npos && pos<basename.size()-1)
6015         {
6016         suffix   = basename.substr(pos+1, basename.size()-pos-1);
6017         basename = basename.substr(0, pos);
6018         }
6020     //trace("parsename:%s %s %s", path.c_str(),
6021     //        basename.c_str(), suffix.c_str()); 
6026 /**
6027  *  Generate our internal file list.
6028  */
6029 bool DepTool::createFileList()
6032     for (unsigned int i=0 ; i<fileList.size() ; i++)
6033         {
6034         String fileName = fileList[i];
6035         //trace("## FileName:%s", fileName.c_str());
6036         String path;
6037         String basename;
6038         String sfx;
6039         parseName(fileName, path, basename, sfx);
6040         if (sfx == "cpp" || sfx == "c" || sfx == "cxx"   ||
6041             sfx == "cc" || sfx == "CC")
6042             {
6043             FileRec *fe         = new FileRec(FileRec::CFILE);
6044             fe->path            = path;
6045             fe->baseName        = basename;
6046             fe->suffix          = sfx;
6047             allFiles[fileName]  = fe;
6048             }
6049         else if (sfx == "h"   ||  sfx == "hh"  ||
6050                  sfx == "hpp" ||  sfx == "hxx")
6051             {
6052             FileRec *fe         = new FileRec(FileRec::HFILE);
6053             fe->path            = path;
6054             fe->baseName        = basename;
6055             fe->suffix          = sfx;
6056             allFiles[fileName]  = fe;
6057             }
6058         }
6060     if (!listDirectories(sourceDir, "", directories))
6061         return false;
6062         
6063     return true;
6070 /**
6071  * Get a character from the buffer at pos.  If out of range,
6072  * return -1 for safety
6073  */
6074 int DepTool::get(int pos)
6076     if (pos>depFileSize)
6077         return -1;
6078     return depFileBuf[pos];
6083 /**
6084  *  Skip over all whitespace characters beginning at pos.  Return
6085  *  the position of the first non-whitespace character.
6086  */
6087 int DepTool::skipwhite(int pos)
6089     while (pos < depFileSize)
6090         {
6091         int ch = get(pos);
6092         if (ch < 0)
6093             break;
6094         if (!isspace(ch))
6095             break;
6096         pos++;
6097         }
6098     return pos;
6102 /**
6103  *  Parse the buffer beginning at pos, for a word.  Fill
6104  *  'ret' with the result.  Return the position after the
6105  *  word.
6106  */
6107 int DepTool::getword(int pos, String &ret)
6109     while (pos < depFileSize)
6110         {
6111         int ch = get(pos);
6112         if (ch < 0)
6113             break;
6114         if (isspace(ch))
6115             break;
6116         ret.push_back((char)ch);
6117         pos++;
6118         }
6119     return pos;
6122 /**
6123  * Return whether the sequence of characters in the buffer
6124  * beginning at pos match the key,  for the length of the key
6125  */
6126 bool DepTool::sequ(int pos, const char *key)
6128     while (*key)
6129         {
6130         if (*key != get(pos))
6131             return false;
6132         key++; pos++;
6133         }
6134     return true;
6139 /**
6140  *  Add an include file name to a file record.  If the name
6141  *  is not found in allFiles explicitly, try prepending include
6142  *  directory names to it and try again.
6143  */
6144 bool DepTool::addIncludeFile(FileRec *frec, const String &iname)
6146     //# if the name is an exact match to a path name
6147     //# in allFiles, like "myinc.h"
6148     std::map<String, FileRec *>::iterator iter =
6149            allFiles.find(iname);
6150     if (iter != allFiles.end()) //already exists
6151         {
6152          //h file in same dir
6153         FileRec *other = iter->second;
6154         //trace("local: '%s'", iname.c_str());
6155         frec->files[iname] = other;
6156         return true;
6157         }
6158     else 
6159         {
6160         //## Ok, it was not found directly
6161         //look in other dirs
6162         std::vector<String>::iterator diter;
6163         for (diter=directories.begin() ;
6164              diter!=directories.end() ; diter++)
6165             {
6166             String dfname = *diter;
6167             dfname.append("/");
6168             dfname.append(iname);
6169             URI fullPathURI(dfname);  //normalize path name
6170             String fullPath = fullPathURI.getPath();
6171             if (fullPath[0] == '/')
6172                 fullPath = fullPath.substr(1);
6173             //trace("Normalized %s to %s", dfname.c_str(), fullPath.c_str());
6174             iter = allFiles.find(fullPath);
6175             if (iter != allFiles.end())
6176                 {
6177                 FileRec *other = iter->second;
6178                 //trace("other: '%s'", iname.c_str());
6179                 frec->files[fullPath] = other;
6180                 return true;
6181                 }
6182             }
6183         }
6184     return true;
6189 /**
6190  *  Lightly parse a file to find the #include directives.  Do
6191  *  a bit of state machine stuff to make sure that the directive
6192  *  is valid.  (Like not in a comment).
6193  */
6194 bool DepTool::scanFile(const String &fname, FileRec *frec)
6196     String fileName;
6197     if (sourceDir.size() > 0)
6198         {
6199         fileName.append(sourceDir);
6200         fileName.append("/");
6201         }
6202     fileName.append(fname);
6203     String nativeName = getNativePath(fileName);
6204     FILE *f = fopen(nativeName.c_str(), "r");
6205     if (!f)
6206         {
6207         error("Could not open '%s' for reading", fname.c_str());
6208         return false;
6209         }
6210     String buf;
6211     while (!feof(f))
6212         {
6213         int nrbytes = fread(readBuf, 1, readBufSize, f);
6214         readBuf[nrbytes] = '\0';
6215         buf.append(readBuf);
6216         }
6217     fclose(f);
6219     depFileSize = buf.size();
6220     depFileBuf  = (char *)buf.c_str();
6221     int pos = 0;
6224     while (pos < depFileSize)
6225         {
6226         //trace("p:%c", get(pos));
6228         //# Block comment
6229         if (get(pos) == '/' && get(pos+1) == '*')
6230             {
6231             pos += 2;
6232             while (pos < depFileSize)
6233                 {
6234                 if (get(pos) == '*' && get(pos+1) == '/')
6235                     {
6236                     pos += 2;
6237                     break;
6238                     }
6239                 else
6240                     pos++;
6241                 }
6242             }
6243         //# Line comment
6244         else if (get(pos) == '/' && get(pos+1) == '/')
6245             {
6246             pos += 2;
6247             while (pos < depFileSize)
6248                 {
6249                 if (get(pos) == '\n')
6250                     {
6251                     pos++;
6252                     break;
6253                     }
6254                 else
6255                     pos++;
6256                 }
6257             }
6258         //# #include! yaay
6259         else if (sequ(pos, "#include"))
6260             {
6261             pos += 8;
6262             pos = skipwhite(pos);
6263             String iname;
6264             pos = getword(pos, iname);
6265             if (iname.size()>2)
6266                 {
6267                 iname = iname.substr(1, iname.size()-2);
6268                 addIncludeFile(frec, iname);
6269                 }
6270             }
6271         else
6272             {
6273             pos++;
6274             }
6275         }
6277     return true;
6282 /**
6283  *  Recursively check include lists to find all files in allFiles to which
6284  *  a given file is dependent.
6285  */
6286 bool DepTool::processDependency(FileRec *ofile, FileRec *include)
6288     std::map<String, FileRec *>::iterator iter;
6289     for (iter=include->files.begin() ; iter!=include->files.end() ; iter++)
6290         {
6291         String fname  = iter->first;
6292         if (ofile->files.find(fname) != ofile->files.end())
6293             {
6294             //trace("file '%s' already seen", fname.c_str());
6295             continue;
6296             }
6297         FileRec *child  = iter->second;
6298         ofile->files[fname] = child;
6299       
6300         processDependency(ofile, child);
6301         }
6304     return true;
6311 /**
6312  *  Generate the file dependency list.
6313  */
6314 bool DepTool::generateDependencies()
6316     std::map<String, FileRec *>::iterator iter;
6317     //# First pass.  Scan for all includes
6318     for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
6319         {
6320         FileRec *frec = iter->second;
6321         if (!scanFile(iter->first, frec))
6322             {
6323             //quit?
6324             }
6325         }
6327     //# Second pass.  Scan for all includes
6328     for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
6329         {
6330         FileRec *include = iter->second;
6331         if (include->type == FileRec::CFILE)
6332             {
6333             //String cFileName   = iter->first;
6334             FileRec *ofile     = new FileRec(FileRec::OFILE);
6335             ofile->path        = include->path;
6336             ofile->baseName    = include->baseName;
6337             ofile->suffix      = include->suffix;
6338             String fname       = include->path;
6339             if (fname.size()>0)
6340                 fname.append("/");
6341             fname.append(include->baseName);
6342             fname.append(".o");
6343             oFiles[fname]    = ofile;
6344             //add the .c file first?   no, don't
6345             //ofile->files[cFileName] = include;
6346             
6347             //trace("ofile:%s", fname.c_str());
6349             processDependency(ofile, include);
6350             }
6351         }
6353       
6354     return true;
6359 /**
6360  *  High-level call to generate deps and optionally save them
6361  */
6362 bool DepTool::generateDependencies(const String &fileName)
6364     if (!createFileList())
6365         return false;
6366     if (!generateDependencies())
6367         return false;
6368     if (!saveDepFile(fileName))
6369         return false;
6370     return true;
6374 /**
6375  *   This saves the dependency cache.
6376  */
6377 bool DepTool::saveDepFile(const String &fileName)
6379     time_t tim;
6380     time(&tim);
6382     FILE *f = fopen(fileName.c_str(), "w");
6383     if (!f)
6384         {
6385         trace("cannot open '%s' for writing", fileName.c_str());
6386         }
6387     fprintf(f, "<?xml version='1.0'?>\n");
6388     fprintf(f, "<!--\n");
6389     fprintf(f, "########################################################\n");
6390     fprintf(f, "## File: build.dep\n");
6391     fprintf(f, "## Generated by BuildTool at :%s", ctime(&tim));
6392     fprintf(f, "########################################################\n");
6393     fprintf(f, "-->\n");
6395     fprintf(f, "<dependencies source='%s'>\n\n", sourceDir.c_str());
6396     std::map<String, FileRec *>::iterator iter;
6397     for (iter=oFiles.begin() ; iter!=oFiles.end() ; iter++)
6398         {
6399         FileRec *frec = iter->second;
6400         if (frec->type == FileRec::OFILE)
6401             {
6402             fprintf(f, "<object path='%s' name='%s' suffix='%s'>\n",
6403                  frec->path.c_str(), frec->baseName.c_str(), frec->suffix.c_str());
6404             std::map<String, FileRec *>::iterator citer;
6405             for (citer=frec->files.begin() ; citer!=frec->files.end() ; citer++)
6406                 {
6407                 String cfname = citer->first;
6408                 fprintf(f, "    <dep name='%s'/>\n", cfname.c_str());
6409                 }
6410             fprintf(f, "</object>\n\n");
6411             }
6412         }
6414     fprintf(f, "</dependencies>\n");
6415     fprintf(f, "\n");
6416     fprintf(f, "<!--\n");
6417     fprintf(f, "########################################################\n");
6418     fprintf(f, "## E N D\n");
6419     fprintf(f, "########################################################\n");
6420     fprintf(f, "-->\n");
6422     fclose(f);
6424     return true;
6430 /**
6431  *   This loads the dependency cache.
6432  */
6433 std::vector<DepRec> DepTool::loadDepFile(const String &depFile)
6435     std::vector<DepRec> result;
6436     
6437     Parser parser;
6438     Element *root = parser.parseFile(depFile.c_str());
6439     if (!root)
6440         {
6441         //error("Could not open %s for reading", depFile.c_str());
6442         return result;
6443         }
6445     if (root->getChildren().size()==0 ||
6446         root->getChildren()[0]->getName()!="dependencies")
6447         {
6448         error("loadDepFile: main xml element should be <dependencies>");
6449         delete root;
6450         return result;
6451         }
6453     //########## Start parsing
6454     Element *depList = root->getChildren()[0];
6456     std::vector<Element *> objects = depList->getChildren();
6457     for (unsigned int i=0 ; i<objects.size() ; i++)
6458         {
6459         Element *objectElem = objects[i];
6460         String tagName = objectElem->getName();
6461         if (tagName != "object")
6462             {
6463             error("loadDepFile: <dependencies> should have only <object> children");
6464             return result;
6465             }
6467         String objName   = objectElem->getAttribute("name");
6468          //trace("object:%s", objName.c_str());
6469         DepRec depObject(objName);
6470         depObject.path   = objectElem->getAttribute("path");
6471         depObject.suffix = objectElem->getAttribute("suffix");
6472         //########## DESCRIPTION
6473         std::vector<Element *> depElems = objectElem->getChildren();
6474         for (unsigned int i=0 ; i<depElems.size() ; i++)
6475             {
6476             Element *depElem = depElems[i];
6477             tagName = depElem->getName();
6478             if (tagName != "dep")
6479                 {
6480                 error("loadDepFile: <object> should have only <dep> children");
6481                 return result;
6482                 }
6483             String depName = depElem->getAttribute("name");
6484             //trace("    dep:%s", depName.c_str());
6485             depObject.files.push_back(depName);
6486             }
6488         //Insert into the result list, in a sorted manner
6489         bool inserted = false;
6490         std::vector<DepRec>::iterator iter;
6491         for (iter = result.begin() ; iter != result.end() ; iter++)
6492             {
6493             String vpath = iter->path;
6494             vpath.append("/");
6495             vpath.append(iter->name);
6496             String opath = depObject.path;
6497             opath.append("/");
6498             opath.append(depObject.name);
6499             if (vpath > opath)
6500                 {
6501                 inserted = true;
6502                 iter = result.insert(iter, depObject);
6503                 break;
6504                 }
6505             }
6506         if (!inserted)
6507             result.push_back(depObject);
6508         }
6510     delete root;
6512     return result;
6516 /**
6517  *   This loads the dependency cache.
6518  */
6519 std::vector<DepRec> DepTool::getDepFile(const String &depFile,
6520                    bool forceRefresh)
6522     std::vector<DepRec> result;
6523     if (forceRefresh)
6524         {
6525         generateDependencies(depFile);
6526         result = loadDepFile(depFile);
6527         }
6528     else
6529         {
6530         //try once
6531         result = loadDepFile(depFile);
6532         if (result.size() == 0)
6533             {
6534             //fail? try again
6535             generateDependencies(depFile);
6536             result = loadDepFile(depFile);
6537             }
6538         }
6539     return result;
6545 //########################################################################
6546 //# T A S K
6547 //########################################################################
6548 //forward decl
6549 class Target;
6550 class Make;
6552 /**
6553  *
6554  */
6555 class Task : public MakeBase
6558 public:
6560     typedef enum
6561         {
6562         TASK_NONE,
6563         TASK_CC,
6564         TASK_COPY,
6565         TASK_CXXTEST_PART,
6566         TASK_CXXTEST_ROOT,
6567         TASK_CXXTEST_RUN,
6568         TASK_DELETE,
6569         TASK_ECHO,
6570         TASK_JAR,
6571         TASK_JAVAC,
6572         TASK_LINK,
6573         TASK_MAKEFILE,
6574         TASK_MKDIR,
6575         TASK_MSGFMT,
6576         TASK_PKG_CONFIG,
6577         TASK_RANLIB,
6578         TASK_RC,
6579         TASK_SHAREDLIB,
6580         TASK_STATICLIB,
6581         TASK_STRIP,
6582         TASK_TOUCH,
6583         TASK_TSTAMP
6584         } TaskType;
6585         
6587     /**
6588      *
6589      */
6590     Task(MakeBase &par) : parent(par)
6591         { init(); }
6593     /**
6594      *
6595      */
6596     Task(const Task &other) : parent(other.parent)
6597         { init(); assign(other); }
6599     /**
6600      *
6601      */
6602     Task &operator=(const Task &other)
6603         { assign(other); return *this; }
6605     /**
6606      *
6607      */
6608     virtual ~Task()
6609         { }
6612     /**
6613      *
6614      */
6615     virtual MakeBase &getParent()
6616         { return parent; }
6618      /**
6619      *
6620      */
6621     virtual int  getType()
6622         { return type; }
6624     /**
6625      *
6626      */
6627     virtual void setType(int val)
6628         { type = val; }
6630     /**
6631      *
6632      */
6633     virtual String getName()
6634         { return name; }
6636     /**
6637      *
6638      */
6639     virtual bool execute()
6640         { return true; }
6642     /**
6643      *
6644      */
6645     virtual bool parse(Element *elem)
6646         { return true; }
6648     /**
6649      *
6650      */
6651     Task *createTask(Element *elem, int lineNr);
6654 protected:
6656     void init()
6657         {
6658         type = TASK_NONE;
6659         name = "none";
6660         }
6662     void assign(const Task &other)
6663         {
6664         type = other.type;
6665         name = other.name;
6666         }
6667         
6668     /**
6669      *  Show task status
6670      */
6671     void taskstatus(const char *fmt, ...)
6672         {
6673         va_list args;
6674         va_start(args,fmt);
6675         fprintf(stdout, "    %s : ", name.c_str());
6676         vfprintf(stdout, fmt, args);
6677         fprintf(stdout, "\n");
6678         va_end(args) ;
6679         }
6681     String getAttribute(Element *elem, const String &attrName)
6682         {
6683         String str;
6684         return str;
6685         }
6687     MakeBase &parent;
6689     int type;
6691     String name;
6692 };
6696 /**
6697  * This task runs the C/C++ compiler.  The compiler is invoked
6698  * for all .c or .cpp files which are newer than their correcsponding
6699  * .o files.  
6700  */
6701 class TaskCC : public Task
6703 public:
6705     TaskCC(MakeBase &par) : Task(par)
6706         {
6707         type = TASK_CC;
6708         name = "cc";
6709         }
6711     virtual ~TaskCC()
6712         {}
6713         
6714     virtual bool isExcludedInc(const String &dirname)
6715         {
6716         for (unsigned int i=0 ; i<excludeInc.size() ; i++)
6717             {
6718             String fname = excludeInc[i];
6719             if (fname == dirname)
6720                 return true;
6721             }
6722         return false;
6723         }
6725     virtual bool execute()
6726         {
6727         //evaluate our parameters
6728         String command         = parent.eval(commandOpt, "gcc");
6729         String ccCommand       = parent.eval(ccCommandOpt, "gcc");
6730         String cxxCommand      = parent.eval(cxxCommandOpt, "g++");
6731         String source          = parent.eval(sourceOpt, ".");
6732         String dest            = parent.eval(destOpt, ".");
6733         String flags           = parent.eval(flagsOpt, "");
6734         String defines         = parent.eval(definesOpt, "");
6735         String includes        = parent.eval(includesOpt, "");
6736         bool continueOnError   = parent.evalBool(continueOnErrorOpt, true);
6737         bool refreshCache      = parent.evalBool(refreshCacheOpt, false);
6739         if (!listFiles(parent, fileSet))
6740             return false;
6741             
6742         FILE *f = NULL;
6743         f = fopen("compile.lst", "w");
6745         //refreshCache is probably false here, unless specified otherwise
6746         String fullName = parent.resolve("build.dep");
6747         if (refreshCache || isNewerThan(parent.getURI().getPath(), fullName))
6748             {
6749             taskstatus("regenerating C/C++ dependency cache");
6750             refreshCache = true;
6751             }
6753         DepTool depTool;
6754         depTool.setSourceDirectory(source);
6755         depTool.setFileList(fileSet.getFiles());
6756         std::vector<DepRec> deps =
6757              depTool.getDepFile("build.dep", refreshCache);
6758         
6759         String incs;
6760         incs.append("-I");
6761         incs.append(parent.resolve("."));
6762         incs.append(" ");
6763         if (includes.size()>0)
6764             {
6765             incs.append(includes);
6766             incs.append(" ");
6767             }
6768         std::set<String> paths;
6769         std::vector<DepRec>::iterator viter;
6770         for (viter=deps.begin() ; viter!=deps.end() ; viter++)
6771             {
6772             DepRec dep = *viter;
6773             if (dep.path.size()>0)
6774                 paths.insert(dep.path);
6775             }
6776         if (source.size()>0)
6777             {
6778             incs.append(" -I");
6779             incs.append(parent.resolve(source));
6780             incs.append(" ");
6781             }
6782         std::set<String>::iterator setIter;
6783         for (setIter=paths.begin() ; setIter!=paths.end() ; setIter++)
6784             {
6785             String dirName = *setIter;
6786             //check excludeInc to see if we dont want to include this dir
6787             if (isExcludedInc(dirName))
6788                 continue;
6789             incs.append(" -I");
6790             String dname;
6791             if (source.size()>0)
6792                 {
6793                 dname.append(source);
6794                 dname.append("/");
6795                 }
6796             dname.append(dirName);
6797             incs.append(parent.resolve(dname));
6798             }
6799             
6800         /**
6801          * Compile each of the C files that need it
6802          */
6803         bool errorOccurred = false;                 
6804         std::vector<String> cfiles;
6805         for (viter=deps.begin() ; viter!=deps.end() ; viter++)
6806             {
6807             DepRec dep = *viter;
6809             //## Select command
6810             String sfx = dep.suffix;
6811             String command = ccCommand;
6812             if (sfx == "cpp" || sfx == "cxx" || sfx == "c++" ||
6813                  sfx == "cc" || sfx == "CC")
6814                 command = cxxCommand;
6815  
6816             //## Make paths
6817             String destPath = dest;
6818             String srcPath  = source;
6819             if (dep.path.size()>0)
6820                 {
6821                 destPath.append("/");
6822                 destPath.append(dep.path);
6823                 srcPath.append("/");
6824                 srcPath.append(dep.path);
6825                 }
6826             //## Make sure destination directory exists
6827             if (!createDirectory(destPath))
6828                 return false;
6829                 
6830             //## Check whether it needs to be done
6831             String destName;
6832             if (destPath.size()>0)
6833                 {
6834                 destName.append(destPath);
6835                 destName.append("/");
6836                 }
6837             destName.append(dep.name);
6838             destName.append(".o");
6839             String destFullName = parent.resolve(destName);
6840             String srcName;
6841             if (srcPath.size()>0)
6842                 {
6843                 srcName.append(srcPath);
6844                 srcName.append("/");
6845                 }
6846             srcName.append(dep.name);
6847             srcName.append(".");
6848             srcName.append(dep.suffix);
6849             String srcFullName = parent.resolve(srcName);
6850             bool compileMe = false;
6851             //# First we check if the source is newer than the .o
6852             if (isNewerThan(srcFullName, destFullName))
6853                 {
6854                 taskstatus("compile of %s required by source: %s",
6855                         destFullName.c_str(), srcFullName.c_str());
6856                 compileMe = true;
6857                 }
6858             else
6859                 {
6860                 //# secondly, we check if any of the included dependencies
6861                 //# of the .c/.cpp is newer than the .o
6862                 for (unsigned int i=0 ; i<dep.files.size() ; i++)
6863                     {
6864                     String depName;
6865                     if (source.size()>0)
6866                         {
6867                         depName.append(source);
6868                         depName.append("/");
6869                         }
6870                     depName.append(dep.files[i]);
6871                     String depFullName = parent.resolve(depName);
6872                     bool depRequires = isNewerThan(depFullName, destFullName);
6873                     //trace("%d %s %s\n", depRequires,
6874                     //        destFullName.c_str(), depFullName.c_str());
6875                     if (depRequires)
6876                         {
6877                         taskstatus("compile of %s required by included: %s",
6878                                 destFullName.c_str(), depFullName.c_str());
6879                         compileMe = true;
6880                         break;
6881                         }
6882                     }
6883                 }
6884             if (!compileMe)
6885                 {
6886                 continue;
6887                 }
6889             //## Assemble the command
6890             String cmd = command;
6891             cmd.append(" -c ");
6892             cmd.append(flags);
6893             cmd.append(" ");
6894             cmd.append(defines);
6895             cmd.append(" ");
6896             cmd.append(incs);
6897             cmd.append(" ");
6898             cmd.append(srcFullName);
6899             cmd.append(" -o ");
6900             cmd.append(destFullName);
6902             //## Execute the command
6904             String outString, errString;
6905             bool ret = executeCommand(cmd.c_str(), "", outString, errString);
6907             if (f)
6908                 {
6909                 fprintf(f, "########################### File : %s\n",
6910                              srcFullName.c_str());
6911                 fprintf(f, "#### COMMAND ###\n");
6912                 int col = 0;
6913                 for (unsigned int i = 0 ; i < cmd.size() ; i++)
6914                     {
6915                     char ch = cmd[i];
6916                     if (isspace(ch)  && col > 63)
6917                         {
6918                         fputc('\n', f);
6919                         col = 0;
6920                         }
6921                     else
6922                         {
6923                         fputc(ch, f);
6924                         col++;
6925                         }
6926                     if (col > 76)
6927                         {
6928                         fputc('\n', f);
6929                         col = 0;
6930                         }
6931                     }
6932                 fprintf(f, "\n");
6933                 fprintf(f, "#### STDOUT ###\n%s\n", outString.c_str());
6934                 fprintf(f, "#### STDERR ###\n%s\n\n", errString.c_str());
6935                 fflush(f);
6936                 }
6937             if (!ret)
6938                 {
6939                 error("problem compiling: %s", errString.c_str());
6940                 errorOccurred = true;
6941                 }
6942             if (errorOccurred && !continueOnError)
6943                 break;
6945             removeFromStatCache(getNativePath(destFullName));
6946             }
6948         if (f)
6949             {
6950             fclose(f);
6951             }
6952         
6953         return !errorOccurred;
6954         }
6957     virtual bool parse(Element *elem)
6958         {
6959         String s;
6960         if (!parent.getAttribute(elem, "command", commandOpt))
6961             return false;
6962         if (commandOpt.size()>0)
6963             { cxxCommandOpt = ccCommandOpt = commandOpt; }
6964         if (!parent.getAttribute(elem, "cc", ccCommandOpt))
6965             return false;
6966         if (!parent.getAttribute(elem, "cxx", cxxCommandOpt))
6967             return false;
6968         if (!parent.getAttribute(elem, "destdir", destOpt))
6969             return false;
6970         if (!parent.getAttribute(elem, "continueOnError", continueOnErrorOpt))
6971             return false;
6972         if (!parent.getAttribute(elem, "refreshCache", refreshCacheOpt))
6973             return false;
6975         std::vector<Element *> children = elem->getChildren();
6976         for (unsigned int i=0 ; i<children.size() ; i++)
6977             {
6978             Element *child = children[i];
6979             String tagName = child->getName();
6980             if (tagName == "flags")
6981                 {
6982                 if (!parent.getValue(child, flagsOpt))
6983                     return false;
6984                 flagsOpt = strip(flagsOpt);
6985                 }
6986             else if (tagName == "includes")
6987                 {
6988                 if (!parent.getValue(child, includesOpt))
6989                     return false;
6990                 includesOpt = strip(includesOpt);
6991                 }
6992             else if (tagName == "defines")
6993                 {
6994                 if (!parent.getValue(child, definesOpt))
6995                     return false;
6996                 definesOpt = strip(definesOpt);
6997                 }
6998             else if (tagName == "fileset")
6999                 {
7000                 if (!parseFileSet(child, parent, fileSet))
7001                     return false;
7002                 sourceOpt = fileSet.getDirectory();
7003                 }
7004             else if (tagName == "excludeinc")
7005                 {
7006                 if (!parseFileList(child, parent, excludeInc))
7007                     return false;
7008                 }
7009             }
7011         return true;
7012         }
7013         
7014 protected:
7016     String   commandOpt;
7017     String   ccCommandOpt;
7018     String   cxxCommandOpt;
7019     String   sourceOpt;
7020     String   destOpt;
7021     String   flagsOpt;
7022     String   definesOpt;
7023     String   includesOpt;
7024     String   continueOnErrorOpt;
7025     String   refreshCacheOpt;
7026     FileSet  fileSet;
7027     FileList excludeInc;
7028     
7029 };
7033 /**
7034  *
7035  */
7036 class TaskCopy : public Task
7038 public:
7040     typedef enum
7041         {
7042         CP_NONE,
7043         CP_TOFILE,
7044         CP_TODIR
7045         } CopyType;
7047     TaskCopy(MakeBase &par) : Task(par)
7048         {
7049         type        = TASK_COPY;
7050         name        = "copy";
7051         cptype      = CP_NONE;
7052         haveFileSet = false;
7053         }
7055     virtual ~TaskCopy()
7056         {}
7058     virtual bool execute()
7059         {
7060         String fileName   = parent.eval(fileNameOpt   , ".");
7061         String toFileName = parent.eval(toFileNameOpt , ".");
7062         String toDirName  = parent.eval(toDirNameOpt  , ".");
7063         bool   verbose    = parent.evalBool(verboseOpt, false);
7064         switch (cptype)
7065            {
7066            case CP_TOFILE:
7067                {
7068                if (fileName.size()>0)
7069                    {
7070                    taskstatus("%s to %s",
7071                         fileName.c_str(), toFileName.c_str());
7072                    String fullSource = parent.resolve(fileName);
7073                    String fullDest = parent.resolve(toFileName);
7074                    if (verbose)
7075                        taskstatus("copy %s to file %s", fullSource.c_str(),
7076                                           fullDest.c_str());
7077                    if (!isRegularFile(fullSource))
7078                        {
7079                        error("copy : file %s does not exist", fullSource.c_str());
7080                        return false;
7081                        }
7082                    if (!isNewerThan(fullSource, fullDest))
7083                        {
7084                        taskstatus("skipped");
7085                        return true;
7086                        }
7087                    if (!copyFile(fullSource, fullDest))
7088                        return false;
7089                    taskstatus("1 file copied");
7090                    }
7091                return true;
7092                }
7093            case CP_TODIR:
7094                {
7095                if (haveFileSet)
7096                    {
7097                    if (!listFiles(parent, fileSet))
7098                        return false;
7099                    String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
7101                    taskstatus("%s to %s",
7102                        fileSetDir.c_str(), toDirName.c_str());
7104                    int nrFiles = 0;
7105                    for (unsigned int i=0 ; i<fileSet.size() ; i++)
7106                        {
7107                        String fileName = fileSet[i];
7109                        String sourcePath;
7110                        if (fileSetDir.size()>0)
7111                            {
7112                            sourcePath.append(fileSetDir);
7113                            sourcePath.append("/");
7114                            }
7115                        sourcePath.append(fileName);
7116                        String fullSource = parent.resolve(sourcePath);
7117                        
7118                        //Get the immediate parent directory's base name
7119                        String baseFileSetDir = fileSetDir;
7120                        unsigned int pos = baseFileSetDir.find_last_of('/');
7121                        if (pos!=baseFileSetDir.npos &&
7122                                   pos < baseFileSetDir.size()-1)
7123                            baseFileSetDir =
7124                               baseFileSetDir.substr(pos+1,
7125                                    baseFileSetDir.size());
7126                        //Now make the new path
7127                        String destPath;
7128                        if (toDirName.size()>0)
7129                            {
7130                            destPath.append(toDirName);
7131                            destPath.append("/");
7132                            }
7133                        if (baseFileSetDir.size()>0)
7134                            {
7135                            destPath.append(baseFileSetDir);
7136                            destPath.append("/");
7137                            }
7138                        destPath.append(fileName);
7139                        String fullDest = parent.resolve(destPath);
7140                        //trace("fileName:%s", fileName.c_str());
7141                        if (verbose)
7142                            taskstatus("copy %s to new dir : %s",
7143                                  fullSource.c_str(), fullDest.c_str());
7144                        if (!isNewerThan(fullSource, fullDest))
7145                            {
7146                            if (verbose)
7147                                taskstatus("copy skipping %s", fullSource.c_str());
7148                            continue;
7149                            }
7150                        if (!copyFile(fullSource, fullDest))
7151                            return false;
7152                        nrFiles++;
7153                        }
7154                    taskstatus("%d file(s) copied", nrFiles);
7155                    }
7156                else //file source
7157                    {
7158                    //For file->dir we want only the basename of
7159                    //the source appended to the dest dir
7160                    taskstatus("%s to %s", 
7161                        fileName.c_str(), toDirName.c_str());
7162                    String baseName = fileName;
7163                    unsigned int pos = baseName.find_last_of('/');
7164                    if (pos!=baseName.npos && pos<baseName.size()-1)
7165                        baseName = baseName.substr(pos+1, baseName.size());
7166                    String fullSource = parent.resolve(fileName);
7167                    String destPath;
7168                    if (toDirName.size()>0)
7169                        {
7170                        destPath.append(toDirName);
7171                        destPath.append("/");
7172                        }
7173                    destPath.append(baseName);
7174                    String fullDest = parent.resolve(destPath);
7175                    if (verbose)
7176                        taskstatus("file %s to new dir : %s", fullSource.c_str(),
7177                                           fullDest.c_str());
7178                    if (!isRegularFile(fullSource))
7179                        {
7180                        error("copy : file %s does not exist", fullSource.c_str());
7181                        return false;
7182                        }
7183                    if (!isNewerThan(fullSource, fullDest))
7184                        {
7185                        taskstatus("skipped");
7186                        return true;
7187                        }
7188                    if (!copyFile(fullSource, fullDest))
7189                        return false;
7190                    taskstatus("1 file copied");
7191                    }
7192                return true;
7193                }
7194            }
7195         return true;
7196         }
7199     virtual bool parse(Element *elem)
7200         {
7201         if (!parent.getAttribute(elem, "file", fileNameOpt))
7202             return false;
7203         if (!parent.getAttribute(elem, "tofile", toFileNameOpt))
7204             return false;
7205         if (toFileNameOpt.size() > 0)
7206             cptype = CP_TOFILE;
7207         if (!parent.getAttribute(elem, "todir", toDirNameOpt))
7208             return false;
7209         if (toDirNameOpt.size() > 0)
7210             cptype = CP_TODIR;
7211         if (!parent.getAttribute(elem, "verbose", verboseOpt))
7212             return false;
7213             
7214         haveFileSet = false;
7215         
7216         std::vector<Element *> children = elem->getChildren();
7217         for (unsigned int i=0 ; i<children.size() ; i++)
7218             {
7219             Element *child = children[i];
7220             String tagName = child->getName();
7221             if (tagName == "fileset")
7222                 {
7223                 if (!parseFileSet(child, parent, fileSet))
7224                     {
7225                     error("problem getting fileset");
7226                     return false;
7227                     }
7228                 haveFileSet = true;
7229                 }
7230             }
7232         //Perform validity checks
7233         if (fileNameOpt.size()>0 && fileSet.size()>0)
7234             {
7235             error("<copy> can only have one of : file= and <fileset>");
7236             return false;
7237             }
7238         if (toFileNameOpt.size()>0 && toDirNameOpt.size()>0)
7239             {
7240             error("<copy> can only have one of : tofile= or todir=");
7241             return false;
7242             }
7243         if (haveFileSet && toDirNameOpt.size()==0)
7244             {
7245             error("a <copy> task with a <fileset> must have : todir=");
7246             return false;
7247             }
7248         if (cptype == CP_TOFILE && fileNameOpt.size()==0)
7249             {
7250             error("<copy> tofile= must be associated with : file=");
7251             return false;
7252             }
7253         if (cptype == CP_TODIR && fileNameOpt.size()==0 && !haveFileSet)
7254             {
7255             error("<copy> todir= must be associated with : file= or <fileset>");
7256             return false;
7257             }
7259         return true;
7260         }
7261         
7262 private:
7264     int cptype;
7265     bool haveFileSet;
7267     FileSet fileSet;
7268     String  fileNameOpt;
7269     String  toFileNameOpt;
7270     String  toDirNameOpt;
7271     String  verboseOpt;
7272 };
7275 /**
7276  * Generate CxxTest files
7277  */
7278 class TaskCxxTestPart: public Task
7280 public:
7282     TaskCxxTestPart(MakeBase &par) : Task(par)
7283          {
7284          type    = TASK_CXXTEST_PART;
7285          name    = "cxxtestpart";
7286          }
7288     virtual ~TaskCxxTestPart()
7289         {}
7291     virtual bool execute()
7292         {
7293         if (!listFiles(parent, fileSet))
7294             return false;
7295         String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
7296                 
7297         String fullDest = parent.resolve(parent.eval(destPathOpt, "."));
7298         String cmd = parent.eval(commandOpt, "cxxtestgen.py");
7299         cmd.append(" --part -o ");
7300         cmd.append(fullDest);
7302         unsigned int newFiles = 0;
7303         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7304             {
7305             String fileName = fileSet[i];
7306             if (getSuffix(fileName) != "h")
7307                 continue;
7308             String sourcePath;
7309             if (fileSetDir.size()>0)
7310                 {
7311                 sourcePath.append(fileSetDir);
7312                 sourcePath.append("/");
7313                 }
7314             sourcePath.append(fileName);
7315             String fullSource = parent.resolve(sourcePath);
7317             cmd.append(" ");
7318             cmd.append(fullSource);
7319             if (isNewerThan(fullSource, fullDest)) newFiles++;
7320             }
7321         
7322         if (newFiles>0) {
7323             size_t const lastSlash = fullDest.find_last_of('/');
7324             if (lastSlash != fullDest.npos) {
7325                 String directory(fullDest, 0, lastSlash);
7326                 if (!createDirectory(directory))
7327                     return false;
7328             }
7330             String outString, errString;
7331             if (!executeCommand(cmd.c_str(), "", outString, errString))
7332                 {
7333                 error("<cxxtestpart> problem: %s", errString.c_str());
7334                 return false;
7335                 }
7336             removeFromStatCache(getNativePath(fullDest));
7337         }
7339         return true;
7340         }
7342     virtual bool parse(Element *elem)
7343         {
7344         if (!parent.getAttribute(elem, "command", commandOpt))
7345             return false;
7346         if (!parent.getAttribute(elem, "out", destPathOpt))
7347             return false;
7348             
7349         std::vector<Element *> children = elem->getChildren();
7350         for (unsigned int i=0 ; i<children.size() ; i++)
7351             {
7352             Element *child = children[i];
7353             String tagName = child->getName();
7354             if (tagName == "fileset")
7355                 {
7356                 if (!parseFileSet(child, parent, fileSet))
7357                     return false;
7358                 }
7359             }
7360         return true;
7361         }
7363 private:
7365     String  commandOpt;
7366     String  destPathOpt;
7367     FileSet fileSet;
7369 };
7372 /**
7373  * Generate the CxxTest root file
7374  */
7375 class TaskCxxTestRoot: public Task
7377 public:
7379     TaskCxxTestRoot(MakeBase &par) : Task(par)
7380          {
7381          type    = TASK_CXXTEST_ROOT;
7382          name    = "cxxtestroot";
7383          }
7385     virtual ~TaskCxxTestRoot()
7386         {}
7388     virtual bool execute()
7389         {
7390         if (!listFiles(parent, fileSet))
7391             return false;
7392         String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
7393         unsigned int newFiles = 0;
7394                 
7395         String fullDest = parent.resolve(parent.eval(destPathOpt, "."));
7396         String cmd = parent.eval(commandOpt, "cxxtestgen.py");
7397         cmd.append(" --root -o ");
7398         cmd.append(fullDest);
7399         String templateFile = parent.eval(templateFileOpt, "");
7400         if (templateFile.size()>0) {
7401             String fullTemplate = parent.resolve(templateFile);
7402             cmd.append(" --template=");
7403             cmd.append(fullTemplate);
7404             if (isNewerThan(fullTemplate, fullDest)) newFiles++;
7405         }
7407         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7408             {
7409             String fileName = fileSet[i];
7410             if (getSuffix(fileName) != "h")
7411                 continue;
7412             String sourcePath;
7413             if (fileSetDir.size()>0)
7414                 {
7415                 sourcePath.append(fileSetDir);
7416                 sourcePath.append("/");
7417                 }
7418             sourcePath.append(fileName);
7419             String fullSource = parent.resolve(sourcePath);
7421             cmd.append(" ");
7422             cmd.append(fullSource);
7423             if (isNewerThan(fullSource, fullDest)) newFiles++;
7424             }
7425         
7426         if (newFiles>0) {
7427             size_t const lastSlash = fullDest.find_last_of('/');
7428             if (lastSlash != fullDest.npos) {
7429                 String directory(fullDest, 0, lastSlash);
7430                 if (!createDirectory(directory))
7431                     return false;
7432             }
7434             String outString, errString;
7435             if (!executeCommand(cmd.c_str(), "", outString, errString))
7436                 {
7437                 error("<cxxtestroot> problem: %s", errString.c_str());
7438                 return false;
7439                 }
7440             removeFromStatCache(getNativePath(fullDest));
7441         }
7443         return true;
7444         }
7446     virtual bool parse(Element *elem)
7447         {
7448         if (!parent.getAttribute(elem, "command", commandOpt))
7449             return false;
7450         if (!parent.getAttribute(elem, "template", templateFileOpt))
7451             return false;
7452         if (!parent.getAttribute(elem, "out", destPathOpt))
7453             return false;
7454             
7455         std::vector<Element *> children = elem->getChildren();
7456         for (unsigned int i=0 ; i<children.size() ; i++)
7457             {
7458             Element *child = children[i];
7459             String tagName = child->getName();
7460             if (tagName == "fileset")
7461                 {
7462                 if (!parseFileSet(child, parent, fileSet))
7463                     return false;
7464                 }
7465             }
7466         return true;
7467         }
7469 private:
7471     String  commandOpt;
7472     String  templateFileOpt;
7473     String  destPathOpt;
7474     FileSet fileSet;
7476 };
7479 /**
7480  * Execute the CxxTest test executable
7481  */
7482 class TaskCxxTestRun: public Task
7484 public:
7486     TaskCxxTestRun(MakeBase &par) : Task(par)
7487          {
7488          type    = TASK_CXXTEST_RUN;
7489          name    = "cxxtestrun";
7490          }
7492     virtual ~TaskCxxTestRun()
7493         {}
7495     virtual bool execute()
7496         {
7497         unsigned int newFiles = 0;
7498                 
7499         String workingDir = parent.resolve(parent.eval(workingDirOpt, "inkscape"));
7500         String rawCmd = parent.eval(commandOpt, "build/cxxtests");
7502         String cmdExe;
7503         if (fileExists(rawCmd)) {
7504             cmdExe = rawCmd;
7505         } else if (fileExists(rawCmd + ".exe")) {
7506             cmdExe = rawCmd + ".exe";
7507         } else {
7508             error("<cxxtestrun> problem: cxxtests executable not found! (command=\"%s\")", rawCmd.c_str());
7509         }
7510         // Note that the log file names are based on the exact name used to call cxxtests (it uses argv[0] + ".log"/".xml")
7511         if (isNewerThan(cmdExe, rawCmd + ".log") || isNewerThan(cmdExe, rawCmd + ".xml")) newFiles++;
7513         // Prepend the necessary ../'s
7514         String cmd = rawCmd;
7515         unsigned int workingDirDepth = 0;
7516         bool wasSlash = true;
7517         for(size_t i=0; i<workingDir.size(); i++) {
7518             // This assumes no . and .. parts
7519             if (wasSlash && workingDir[i]!='/') workingDirDepth++;
7520             wasSlash = workingDir[i] == '/';
7521         }
7522         for(size_t i=0; i<workingDirDepth; i++) {
7523             cmd = "../" + cmd;
7524         }
7525         
7526         if (newFiles>0) {
7527             char olddir[1024];
7528             if (workingDir.size()>0) {
7529                 // TODO: Double-check usage of getcwd and handle chdir errors
7530                 getcwd(olddir, 1024);
7531                 chdir(workingDir.c_str());
7532             }
7534             String outString;
7535             if (!executeCommand(cmd.c_str(), "", outString, outString))
7536                 {
7537                 error("<cxxtestrun> problem: %s", outString.c_str());
7538                 return false;
7539                 }
7541             if (workingDir.size()>0) {
7542                 // TODO: Handle errors?
7543                 chdir(olddir);
7544             }
7546             removeFromStatCache(getNativePath(cmd + ".log"));
7547             removeFromStatCache(getNativePath(cmd + ".xml"));
7548         }
7550         return true;
7551         }
7553     virtual bool parse(Element *elem)
7554         {
7555         if (!parent.getAttribute(elem, "command", commandOpt))
7556             return false;
7557         if (!parent.getAttribute(elem, "workingdir", workingDirOpt))
7558             return false;
7559         return true;
7560         }
7562 private:
7564     String  commandOpt;
7565     String  workingDirOpt;
7567 };
7570 /**
7571  *
7572  */
7573 class TaskDelete : public Task
7575 public:
7577     typedef enum
7578         {
7579         DEL_FILE,
7580         DEL_DIR,
7581         DEL_FILESET
7582         } DeleteType;
7584     TaskDelete(MakeBase &par) : Task(par)
7585         { 
7586         type        = TASK_DELETE;
7587         name        = "delete";
7588         delType     = DEL_FILE;
7589         }
7591     virtual ~TaskDelete()
7592         {}
7594     virtual bool execute()
7595         {
7596         String dirName   = parent.eval(dirNameOpt, ".");
7597         String fileName  = parent.eval(fileNameOpt, ".");
7598         bool verbose     = parent.evalBool(verboseOpt, false);
7599         bool quiet       = parent.evalBool(quietOpt, false);
7600         bool failOnError = parent.evalBool(failOnErrorOpt, true);
7601         struct stat finfo;
7602         switch (delType)
7603             {
7604             case DEL_FILE:
7605                 {
7606                 taskstatus("file: %s", fileName.c_str());
7607                 String fullName = parent.resolve(fileName);
7608                 char *fname = (char *)fullName.c_str();
7609                 if (!quiet && verbose)
7610                     taskstatus("path: %s", fname);
7611                 if (!removeFile(fullName)) return false;
7612                 return true;
7613                 }
7614             case DEL_DIR:
7615                 {
7616                 taskstatus("dir: %s", dirName.c_str());
7617                 String fullDir = parent.resolve(dirName);
7618                 if (!quiet && verbose)
7619                     taskstatus("path: %s", fullDir.c_str());
7620                 if (!removeDirectory(fullDir))
7621                     return false;
7622                 return true;
7623                 }
7624             }
7625         return true;
7626         }
7628     virtual bool parse(Element *elem)
7629         {
7630         if (!parent.getAttribute(elem, "file", fileNameOpt))
7631             return false;
7632         if (fileNameOpt.size() > 0)
7633             delType = DEL_FILE;
7634         if (!parent.getAttribute(elem, "dir", dirNameOpt))
7635             return false;
7636         if (dirNameOpt.size() > 0)
7637             delType = DEL_DIR;
7638         if (fileNameOpt.size()>0 && dirNameOpt.size()>0)
7639             {
7640             error("<delete> can have one attribute of file= or dir=");
7641             return false;
7642             }
7643         if (fileNameOpt.size()==0 && dirNameOpt.size()==0)
7644             {
7645             error("<delete> must have one attribute of file= or dir=");
7646             return false;
7647             }
7648         if (!parent.getAttribute(elem, "verbose", verboseOpt))
7649             return false;
7650         if (!parent.getAttribute(elem, "quiet", quietOpt))
7651             return false;
7652         if (!parent.getAttribute(elem, "failonerror", failOnErrorOpt))
7653             return false;
7654         return true;
7655         }
7657 private:
7659     int delType;
7660     String dirNameOpt;
7661     String fileNameOpt;
7662     String verboseOpt;
7663     String quietOpt;
7664     String failOnErrorOpt;
7665 };
7668 /**
7669  * Send a message to stdout
7670  */
7671 class TaskEcho : public Task
7673 public:
7675     TaskEcho(MakeBase &par) : Task(par)
7676         { type = TASK_ECHO; name = "echo"; }
7678     virtual ~TaskEcho()
7679         {}
7681     virtual bool execute()
7682         {
7683         //let message have priority over text
7684         String message = parent.eval(messageOpt, "");
7685         String text    = parent.eval(textOpt, "");
7686         if (message.size() > 0)
7687             {
7688             fprintf(stdout, "%s\n", message.c_str());
7689             }
7690         else if (text.size() > 0)
7691             {
7692             fprintf(stdout, "%s\n", text.c_str());
7693             }
7694         return true;
7695         }
7697     virtual bool parse(Element *elem)
7698         {
7699         if (!parent.getValue(elem, textOpt))
7700             return false;
7701         textOpt    = leftJustify(textOpt);
7702         if (!parent.getAttribute(elem, "message", messageOpt))
7703             return false;
7704         return true;
7705         }
7707 private:
7709     String messageOpt;
7710     String textOpt;
7711 };
7715 /**
7716  *
7717  */
7718 class TaskJar : public Task
7720 public:
7722     TaskJar(MakeBase &par) : Task(par)
7723         { type = TASK_JAR; name = "jar"; }
7725     virtual ~TaskJar()
7726         {}
7728     virtual bool execute()
7729         {
7730         String command  = parent.eval(commandOpt, "jar");
7731         String basedir  = parent.eval(basedirOpt, ".");
7732         String destfile = parent.eval(destfileOpt, ".");
7734         String cmd = command;
7735         cmd.append(" -cf ");
7736         cmd.append(destfile);
7737         cmd.append(" -C ");
7738         cmd.append(basedir);
7739         cmd.append(" .");
7741         String execCmd = cmd;
7743         String outString, errString;
7744         bool ret = executeCommand(execCmd.c_str(), "", outString, errString);
7745         if (!ret)
7746             {
7747             error("<jar> command '%s' failed :\n %s",
7748                                       execCmd.c_str(), errString.c_str());
7749             return false;
7750             }
7751         removeFromStatCache(getNativePath(destfile));
7752         return true;
7753         }
7755     virtual bool parse(Element *elem)
7756         {
7757         if (!parent.getAttribute(elem, "command", commandOpt))
7758             return false;
7759         if (!parent.getAttribute(elem, "basedir", basedirOpt))
7760             return false;
7761         if (!parent.getAttribute(elem, "destfile", destfileOpt))
7762             return false;
7763         if (basedirOpt.size() == 0 || destfileOpt.size() == 0)
7764             {
7765             error("<jar> required both basedir and destfile attributes to be set");
7766             return false;
7767             }
7768         return true;
7769         }
7771 private:
7773     String commandOpt;
7774     String basedirOpt;
7775     String destfileOpt;
7776 };
7779 /**
7780  *
7781  */
7782 class TaskJavac : public Task
7784 public:
7786     TaskJavac(MakeBase &par) : Task(par)
7787         { 
7788         type = TASK_JAVAC; name = "javac";
7789         }
7791     virtual ~TaskJavac()
7792         {}
7794     virtual bool execute()
7795         {
7796         String command  = parent.eval(commandOpt, "javac");
7797         String srcdir   = parent.eval(srcdirOpt, ".");
7798         String destdir  = parent.eval(destdirOpt, ".");
7799         String target   = parent.eval(targetOpt, "");
7801         std::vector<String> fileList;
7802         if (!listFiles(srcdir, "", fileList))
7803             {
7804             return false;
7805             }
7806         String cmd = command;
7807         cmd.append(" -d ");
7808         cmd.append(destdir);
7809         cmd.append(" -classpath ");
7810         cmd.append(destdir);
7811         cmd.append(" -sourcepath ");
7812         cmd.append(srcdir);
7813         cmd.append(" ");
7814         if (target.size()>0)
7815             {
7816             cmd.append(" -target ");
7817             cmd.append(target);
7818             cmd.append(" ");
7819             }
7820         String fname = "javalist.btool";
7821         FILE *f = fopen(fname.c_str(), "w");
7822         int count = 0;
7823         for (unsigned int i=0 ; i<fileList.size() ; i++)
7824             {
7825             String fname = fileList[i];
7826             String srcName = fname;
7827             if (fname.size()<6) //x.java
7828                 continue;
7829             if (fname.compare(fname.size()-5, 5, ".java") != 0)
7830                 continue;
7831             String baseName = fname.substr(0, fname.size()-5);
7832             String destName = baseName;
7833             destName.append(".class");
7835             String fullSrc = srcdir;
7836             fullSrc.append("/");
7837             fullSrc.append(fname);
7838             String fullDest = destdir;
7839             fullDest.append("/");
7840             fullDest.append(destName);
7841             //trace("fullsrc:%s fulldest:%s", fullSrc.c_str(), fullDest.c_str());
7842             if (!isNewerThan(fullSrc, fullDest))
7843                 continue;
7845             count++;
7846             fprintf(f, "%s\n", fullSrc.c_str());
7847             }
7848         fclose(f);
7849         if (!count)
7850             {
7851             taskstatus("nothing to do");
7852             return true;
7853             }
7855         taskstatus("compiling %d files", count);
7857         String execCmd = cmd;
7858         execCmd.append("@");
7859         execCmd.append(fname);
7861         String outString, errString;
7862         bool ret = executeCommand(execCmd.c_str(), "", outString, errString);
7863         if (!ret)
7864             {
7865             error("<javac> command '%s' failed :\n %s",
7866                                       execCmd.c_str(), errString.c_str());
7867             return false;
7868             }
7869         // TODO: 
7870         //removeFromStatCache(getNativePath(........));
7871         return true;
7872         }
7874     virtual bool parse(Element *elem)
7875         {
7876         if (!parent.getAttribute(elem, "command", commandOpt))
7877             return false;
7878         if (!parent.getAttribute(elem, "srcdir", srcdirOpt))
7879             return false;
7880         if (!parent.getAttribute(elem, "destdir", destdirOpt))
7881             return false;
7882         if (srcdirOpt.size() == 0 || destdirOpt.size() == 0)
7883             {
7884             error("<javac> required both srcdir and destdir attributes to be set");
7885             return false;
7886             }
7887         if (!parent.getAttribute(elem, "target", targetOpt))
7888             return false;
7889         return true;
7890         }
7892 private:
7894     String commandOpt;
7895     String srcdirOpt;
7896     String destdirOpt;
7897     String targetOpt;
7899 };
7902 /**
7903  *
7904  */
7905 class TaskLink : public Task
7907 public:
7909     TaskLink(MakeBase &par) : Task(par)
7910         {
7911         type = TASK_LINK; name = "link";
7912         }
7914     virtual ~TaskLink()
7915         {}
7917     virtual bool execute()
7918         {
7919         String  command        = parent.eval(commandOpt, "g++");
7920         String  fileName       = parent.eval(fileNameOpt, "");
7921         String  flags          = parent.eval(flagsOpt, "");
7922         String  libs           = parent.eval(libsOpt, "");
7923         bool    doStrip        = parent.evalBool(doStripOpt, false);
7924         String  symFileName    = parent.eval(symFileNameOpt, "");
7925         String  stripCommand   = parent.eval(stripCommandOpt, "strip");
7926         String  objcopyCommand = parent.eval(objcopyCommandOpt, "objcopy");
7928         if (!listFiles(parent, fileSet))
7929             return false;
7930         String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
7931         //trace("%d files in %s", fileSet.size(), fileSetDir.c_str());
7932         bool doit = false;
7933         String fullTarget = parent.resolve(fileName);
7934         String cmd = command;
7935         cmd.append(" -o ");
7936         cmd.append(fullTarget);
7937         cmd.append(" ");
7938         cmd.append(flags);
7939         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7940             {
7941             cmd.append(" ");
7942             String obj;
7943             if (fileSetDir.size()>0)
7944                 {
7945                 obj.append(fileSetDir);
7946                 obj.append("/");
7947                 }
7948             obj.append(fileSet[i]);
7949             String fullObj = parent.resolve(obj);
7950             String nativeFullObj = getNativePath(fullObj);
7951             cmd.append(nativeFullObj);
7952             //trace("link: tgt:%s obj:%s", fullTarget.c_str(),
7953             //          fullObj.c_str());
7954             if (isNewerThan(fullObj, fullTarget))
7955                 doit = true;
7956             }
7957         cmd.append(" ");
7958         cmd.append(libs);
7959         if (!doit)
7960             {
7961             //trace("link not needed");
7962             return true;
7963             }
7964         //trace("LINK cmd:%s", cmd.c_str());
7967         String outbuf, errbuf;
7968         if (!executeCommand(cmd.c_str(), "", outbuf, errbuf))
7969             {
7970             error("LINK problem: %s", errbuf.c_str());
7971             return false;
7972             }
7973         removeFromStatCache(getNativePath(fullTarget));
7975         if (symFileName.size()>0)
7976             {
7977             String symFullName = parent.resolve(symFileName);
7978             cmd = objcopyCommand;
7979             cmd.append(" --only-keep-debug ");
7980             cmd.append(getNativePath(fullTarget));
7981             cmd.append(" ");
7982             cmd.append(getNativePath(symFullName));
7983             if (!executeCommand(cmd, "", outbuf, errbuf))
7984                 {
7985                 error("<strip> symbol file failed : %s", errbuf.c_str());
7986                 return false;
7987                 }
7988             removeFromStatCache(getNativePath(symFullName));
7989             }
7990             
7991         if (doStrip)
7992             {
7993             cmd = stripCommand;
7994             cmd.append(" ");
7995             cmd.append(getNativePath(fullTarget));
7996             if (!executeCommand(cmd, "", outbuf, errbuf))
7997                {
7998                error("<strip> failed : %s", errbuf.c_str());
7999                return false;
8000                }
8001             removeFromStatCache(getNativePath(fullTarget));
8002             }
8004         return true;
8005         }
8007     virtual bool parse(Element *elem)
8008         {
8009         if (!parent.getAttribute(elem, "command", commandOpt))
8010             return false;
8011         if (!parent.getAttribute(elem, "objcopycommand", objcopyCommandOpt))
8012             return false;
8013         if (!parent.getAttribute(elem, "stripcommand", stripCommandOpt))
8014             return false;
8015         if (!parent.getAttribute(elem, "out", fileNameOpt))
8016             return false;
8017         if (!parent.getAttribute(elem, "strip", doStripOpt))
8018             return false;
8019         if (!parent.getAttribute(elem, "symfile", symFileNameOpt))
8020             return false;
8021             
8022         std::vector<Element *> children = elem->getChildren();
8023         for (unsigned int i=0 ; i<children.size() ; i++)
8024             {
8025             Element *child = children[i];
8026             String tagName = child->getName();
8027             if (tagName == "fileset")
8028                 {
8029                 if (!parseFileSet(child, parent, fileSet))
8030                     return false;
8031                 }
8032             else if (tagName == "flags")
8033                 {
8034                 if (!parent.getValue(child, flagsOpt))
8035                     return false;
8036                 flagsOpt = strip(flagsOpt);
8037                 }
8038             else if (tagName == "libs")
8039                 {
8040                 if (!parent.getValue(child, libsOpt))
8041                     return false;
8042                 libsOpt = strip(libsOpt);
8043                 }
8044             }
8045         return true;
8046         }
8048 private:
8050     FileSet fileSet;
8052     String  commandOpt;
8053     String  fileNameOpt;
8054     String  flagsOpt;
8055     String  libsOpt;
8056     String  doStripOpt;
8057     String  symFileNameOpt;
8058     String  stripCommandOpt;
8059     String  objcopyCommandOpt;
8061 };
8065 /**
8066  * Create a named file
8067  */
8068 class TaskMakeFile : public Task
8070 public:
8072     TaskMakeFile(MakeBase &par) : Task(par)
8073         { type = TASK_MAKEFILE; name = "makefile"; }
8075     virtual ~TaskMakeFile()
8076         {}
8078     virtual bool execute()
8079         {
8080         String fileName = parent.eval(fileNameOpt, "");
8081         String text     = parent.eval(textOpt, "");
8083         taskstatus("%s", fileName.c_str());
8084         String fullName = parent.resolve(fileName);
8085         if (!isNewerThan(parent.getURI().getPath(), fullName))
8086             {
8087             //trace("skipped <makefile>");
8088             return true;
8089             }
8090         String fullNative = getNativePath(fullName);
8091         //trace("fullName:%s", fullName.c_str());
8092         FILE *f = fopen(fullNative.c_str(), "w");
8093         if (!f)
8094             {
8095             error("<makefile> could not open %s for writing : %s",
8096                 fullName.c_str(), strerror(errno));
8097             return false;
8098             }
8099         for (unsigned int i=0 ; i<text.size() ; i++)
8100             fputc(text[i], f);
8101         fputc('\n', f);
8102         fclose(f);
8103         removeFromStatCache(fullNative);
8104         return true;
8105         }
8107     virtual bool parse(Element *elem)
8108         {
8109         if (!parent.getAttribute(elem, "file", fileNameOpt))
8110             return false;
8111         if (fileNameOpt.size() == 0)
8112             {
8113             error("<makefile> requires 'file=\"filename\"' attribute");
8114             return false;
8115             }
8116         if (!parent.getValue(elem, textOpt))
8117             return false;
8118         textOpt = leftJustify(textOpt);
8119         //trace("dirname:%s", dirName.c_str());
8120         return true;
8121         }
8123 private:
8125     String fileNameOpt;
8126     String textOpt;
8127 };
8131 /**
8132  * Create a named directory
8133  */
8134 class TaskMkDir : public Task
8136 public:
8138     TaskMkDir(MakeBase &par) : Task(par)
8139         { type = TASK_MKDIR; name = "mkdir"; }
8141     virtual ~TaskMkDir()
8142         {}
8144     virtual bool execute()
8145         {
8146         String dirName = parent.eval(dirNameOpt, ".");
8147         
8148         taskstatus("%s", dirName.c_str());
8149         String fullDir = parent.resolve(dirName);
8150         //trace("fullDir:%s", fullDir.c_str());
8151         if (!createDirectory(fullDir))
8152             return false;
8153         return true;
8154         }
8156     virtual bool parse(Element *elem)
8157         {
8158         if (!parent.getAttribute(elem, "dir", dirNameOpt))
8159             return false;
8160         if (dirNameOpt.size() == 0)
8161             {
8162             error("<mkdir> requires 'dir=\"dirname\"' attribute");
8163             return false;
8164             }
8165         return true;
8166         }
8168 private:
8170     String dirNameOpt;
8171 };
8175 /**
8176  * Create a named directory
8177  */
8178 class TaskMsgFmt: public Task
8180 public:
8182     TaskMsgFmt(MakeBase &par) : Task(par)
8183          { type = TASK_MSGFMT;  name = "msgfmt"; }
8185     virtual ~TaskMsgFmt()
8186         {}
8188     virtual bool execute()
8189         {
8190         String  command   = parent.eval(commandOpt, "msgfmt");
8191         String  toDirName = parent.eval(toDirNameOpt, ".");
8192         String  outName   = parent.eval(outNameOpt, "");
8193         bool    owndir    = parent.evalBool(owndirOpt, false);
8195         if (!listFiles(parent, fileSet))
8196             return false;
8197         String fileSetDir = fileSet.getDirectory();
8199         //trace("msgfmt: %d", fileSet.size());
8200         for (unsigned int i=0 ; i<fileSet.size() ; i++)
8201             {
8202             String fileName = fileSet[i];
8203             if (getSuffix(fileName) != "po")
8204                 continue;
8205             String sourcePath;
8206             if (fileSetDir.size()>0)
8207                 {
8208                 sourcePath.append(fileSetDir);
8209                 sourcePath.append("/");
8210                 }
8211             sourcePath.append(fileName);
8212             String fullSource = parent.resolve(sourcePath);
8214             String destPath;
8215             if (toDirName.size()>0)
8216                 {
8217                 destPath.append(toDirName);
8218                 destPath.append("/");
8219                 }
8220             if (owndir)
8221                 {
8222                 String subdir = fileName;
8223                 unsigned int pos = subdir.find_last_of('.');
8224                 if (pos != subdir.npos)
8225                     subdir = subdir.substr(0, pos);
8226                 destPath.append(subdir);
8227                 destPath.append("/");
8228                 }
8229             //Pick the output file name
8230             if (outName.size() > 0)
8231                 {
8232                 destPath.append(outName);
8233                 }
8234             else
8235                 {
8236                 destPath.append(fileName);
8237                 destPath[destPath.size()-2] = 'm';
8238                 }
8240             String fullDest = parent.resolve(destPath);
8242             if (!isNewerThan(fullSource, fullDest))
8243                 {
8244                 //trace("skip %s", fullSource.c_str());
8245                 continue;
8246                 }
8247                 
8248             String cmd = command;
8249             cmd.append(" ");
8250             cmd.append(fullSource);
8251             cmd.append(" -o ");
8252             cmd.append(fullDest);
8253             
8254             int pos = fullDest.find_last_of('/');
8255             if (pos>0)
8256                 {
8257                 String fullDestPath = fullDest.substr(0, pos);
8258                 if (!createDirectory(fullDestPath))
8259                     return false;
8260                 }
8264             String outString, errString;
8265             if (!executeCommand(cmd.c_str(), "", outString, errString))
8266                 {
8267                 error("<msgfmt> problem: %s", errString.c_str());
8268                 return false;
8269                 }
8270             removeFromStatCache(getNativePath(fullDest));
8271             }
8273         return true;
8274         }
8276     virtual bool parse(Element *elem)
8277         {
8278         if (!parent.getAttribute(elem, "command", commandOpt))
8279             return false;
8280         if (!parent.getAttribute(elem, "todir", toDirNameOpt))
8281             return false;
8282         if (!parent.getAttribute(elem, "out", outNameOpt))
8283             return false;
8284         if (!parent.getAttribute(elem, "owndir", owndirOpt))
8285             return false;
8286             
8287         std::vector<Element *> children = elem->getChildren();
8288         for (unsigned int i=0 ; i<children.size() ; i++)
8289             {
8290             Element *child = children[i];
8291             String tagName = child->getName();
8292             if (tagName == "fileset")
8293                 {
8294                 if (!parseFileSet(child, parent, fileSet))
8295                     return false;
8296                 }
8297             }
8298         return true;
8299         }
8301 private:
8303     FileSet fileSet;
8305     String  commandOpt;
8306     String  toDirNameOpt;
8307     String  outNameOpt;
8308     String  owndirOpt;
8310 };
8314 /**
8315  *  Perform a Package-Config query similar to pkg-config
8316  */
8317 class TaskPkgConfig : public Task
8319 public:
8321     typedef enum
8322         {
8323         PKG_CONFIG_QUERY_CFLAGS,
8324         PKG_CONFIG_QUERY_LIBS,
8325         PKG_CONFIG_QUERY_ALL
8326         } QueryTypes;
8328     TaskPkgConfig(MakeBase &par) : Task(par)
8329         {
8330         type = TASK_PKG_CONFIG;
8331         name = "pkg-config";
8332         }
8334     virtual ~TaskPkgConfig()
8335         {}
8337     virtual bool execute()
8338         {
8339         String pkgName       = parent.eval(pkgNameOpt,      "");
8340         String prefix        = parent.eval(prefixOpt,       "");
8341         String propName      = parent.eval(propNameOpt,     "");
8342         String pkgConfigPath = parent.eval(pkgConfigPathOpt,"");
8343         String query         = parent.eval(queryOpt,        "all");
8345         String path = parent.resolve(pkgConfigPath);
8346         PkgConfig pkgconfig;
8347         pkgconfig.setPath(path);
8348         pkgconfig.setPrefix(prefix);
8349         if (!pkgconfig.query(pkgName))
8350             {
8351             error("<pkg-config> query failed for '%s", name.c_str());
8352             return false;
8353             }
8354             
8355         String val = "";
8356         if (query == "cflags")
8357             val = pkgconfig.getCflags();
8358         else if (query == "libs")
8359             val =pkgconfig.getLibs();
8360         else if (query == "all")
8361             val = pkgconfig.getAll();
8362         else
8363             {
8364             error("<pkg-config> unhandled query : %s", query.c_str());
8365             return false;
8366             }
8367         taskstatus("property %s = '%s'", propName.c_str(), val.c_str());
8368         parent.setProperty(propName, val);
8369         return true;
8370         }
8372     virtual bool parse(Element *elem)
8373         {
8374         //# NAME
8375         if (!parent.getAttribute(elem, "name", pkgNameOpt))
8376             return false;
8377         if (pkgNameOpt.size()==0)
8378             {
8379             error("<pkg-config> requires 'name=\"package\"' attribute");
8380             return false;
8381             }
8383         //# PROPERTY
8384         if (!parent.getAttribute(elem, "property", propNameOpt))
8385             return false;
8386         if (propNameOpt.size()==0)
8387             {
8388             error("<pkg-config> requires 'property=\"name\"' attribute");
8389             return false;
8390             }
8391         //# PATH
8392         if (!parent.getAttribute(elem, "path", pkgConfigPathOpt))
8393             return false;
8394         //# PREFIX
8395         if (!parent.getAttribute(elem, "prefix", prefixOpt))
8396             return false;
8397         //# QUERY
8398         if (!parent.getAttribute(elem, "query", queryOpt))
8399             return false;
8401         return true;
8402         }
8404 private:
8406     String queryOpt;
8407     String pkgNameOpt;
8408     String prefixOpt;
8409     String propNameOpt;
8410     String pkgConfigPathOpt;
8412 };
8419 /**
8420  *  Process an archive to allow random access
8421  */
8422 class TaskRanlib : public Task
8424 public:
8426     TaskRanlib(MakeBase &par) : Task(par)
8427         { type = TASK_RANLIB; name = "ranlib"; }
8429     virtual ~TaskRanlib()
8430         {}
8432     virtual bool execute()
8433         {
8434         String fileName = parent.eval(fileNameOpt, "");
8435         String command  = parent.eval(commandOpt, "ranlib");
8437         String fullName = parent.resolve(fileName);
8438         //trace("fullDir:%s", fullDir.c_str());
8439         String cmd = command;
8440         cmd.append(" ");
8441         cmd.append(fullName);
8442         String outbuf, errbuf;
8443         if (!executeCommand(cmd, "", outbuf, errbuf))
8444             return false;
8445         // TODO:
8446         //removeFromStatCache(getNativePath(fullDest));
8447         return true;
8448         }
8450     virtual bool parse(Element *elem)
8451         {
8452         if (!parent.getAttribute(elem, "command", commandOpt))
8453             return false;
8454         if (!parent.getAttribute(elem, "file", fileNameOpt))
8455             return false;
8456         if (fileNameOpt.size() == 0)
8457             {
8458             error("<ranlib> requires 'file=\"fileNname\"' attribute");
8459             return false;
8460             }
8461         return true;
8462         }
8464 private:
8466     String fileNameOpt;
8467     String commandOpt;
8468 };
8472 /**
8473  * Compile a resource file into a binary object
8474  */
8475 class TaskRC : public Task
8477 public:
8479     TaskRC(MakeBase &par) : Task(par)
8480         { type = TASK_RC; name = "rc"; }
8482     virtual ~TaskRC()
8483         {}
8485     virtual bool execute()
8486         {
8487         String command  = parent.eval(commandOpt,  "windres");
8488         String flags    = parent.eval(flagsOpt,    "");
8489         String fileName = parent.eval(fileNameOpt, "");
8490         String outName  = parent.eval(outNameOpt,  "");
8492         String fullFile = parent.resolve(fileName);
8493         String fullOut  = parent.resolve(outName);
8494         if (!isNewerThan(fullFile, fullOut))
8495             return true;
8496         String cmd = command;
8497         cmd.append(" -o ");
8498         cmd.append(fullOut);
8499         cmd.append(" ");
8500         cmd.append(flags);
8501         cmd.append(" ");
8502         cmd.append(fullFile);
8504         String outString, errString;
8505         if (!executeCommand(cmd.c_str(), "", outString, errString))
8506             {
8507             error("RC problem: %s", errString.c_str());
8508             return false;
8509             }
8510         removeFromStatCache(getNativePath(fullOut));
8511         return true;
8512         }
8514     virtual bool parse(Element *elem)
8515         {
8516         if (!parent.getAttribute(elem, "command", commandOpt))
8517             return false;
8518         if (!parent.getAttribute(elem, "file", fileNameOpt))
8519             return false;
8520         if (!parent.getAttribute(elem, "out", outNameOpt))
8521             return false;
8522         std::vector<Element *> children = elem->getChildren();
8523         for (unsigned int i=0 ; i<children.size() ; i++)
8524             {
8525             Element *child = children[i];
8526             String tagName = child->getName();
8527             if (tagName == "flags")
8528                 {
8529                 if (!parent.getValue(child, flagsOpt))
8530                     return false;
8531                 }
8532             }
8533         return true;
8534         }
8536 private:
8538     String commandOpt;
8539     String flagsOpt;
8540     String fileNameOpt;
8541     String outNameOpt;
8543 };
8547 /**
8548  *  Collect .o's into a .so or DLL
8549  */
8550 class TaskSharedLib : public Task
8552 public:
8554     TaskSharedLib(MakeBase &par) : Task(par)
8555         { type = TASK_SHAREDLIB; name = "dll"; }
8557     virtual ~TaskSharedLib()
8558         {}
8560     virtual bool execute()
8561         {
8562         String command     = parent.eval(commandOpt, "dllwrap");
8563         String fileName    = parent.eval(fileNameOpt, "");
8564         String defFileName = parent.eval(defFileNameOpt, "");
8565         String impFileName = parent.eval(impFileNameOpt, "");
8566         String libs        = parent.eval(libsOpt, "");
8568         //trace("###########HERE %d", fileSet.size());
8569         bool doit = false;
8570         
8571         String fullOut = parent.resolve(fileName);
8572         //trace("ar fullout: %s", fullOut.c_str());
8573         
8574         if (!listFiles(parent, fileSet))
8575             return false;
8576         String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
8578         for (unsigned int i=0 ; i<fileSet.size() ; i++)
8579             {
8580             String fname;
8581             if (fileSetDir.size()>0)
8582                 {
8583                 fname.append(fileSetDir);
8584                 fname.append("/");
8585                 }
8586             fname.append(fileSet[i]);
8587             String fullName = parent.resolve(fname);
8588             //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
8589             if (isNewerThan(fullName, fullOut))
8590                 doit = true;
8591             }
8592         //trace("Needs it:%d", doit);
8593         if (!doit)
8594             {
8595             return true;
8596             }
8598         String cmd = "dllwrap";
8599         cmd.append(" -o ");
8600         cmd.append(fullOut);
8601         if (defFileName.size()>0)
8602             {
8603             cmd.append(" --def ");
8604             cmd.append(defFileName);
8605             cmd.append(" ");
8606             }
8607         if (impFileName.size()>0)
8608             {
8609             cmd.append(" --implib ");
8610             cmd.append(impFileName);
8611             cmd.append(" ");
8612             }
8613         for (unsigned int i=0 ; i<fileSet.size() ; i++)
8614             {
8615             String fname;
8616             if (fileSetDir.size()>0)
8617                 {
8618                 fname.append(fileSetDir);
8619                 fname.append("/");
8620                 }
8621             fname.append(fileSet[i]);
8622             String fullName = parent.resolve(fname);
8624             cmd.append(" ");
8625             cmd.append(fullName);
8626             }
8627         cmd.append(" ");
8628         cmd.append(libs);
8630         String outString, errString;
8631         if (!executeCommand(cmd.c_str(), "", outString, errString))
8632             {
8633             error("<sharedlib> problem: %s", errString.c_str());
8634             return false;
8635             }
8636         removeFromStatCache(getNativePath(fullOut));
8637         return true;
8638         }
8640     virtual bool parse(Element *elem)
8641         {
8642         if (!parent.getAttribute(elem, "command", commandOpt))
8643             return false;
8644         if (!parent.getAttribute(elem, "file", fileNameOpt))
8645             return false;
8646         if (!parent.getAttribute(elem, "import", impFileNameOpt))
8647             return false;
8648         if (!parent.getAttribute(elem, "def", defFileNameOpt))
8649             return false;
8650             
8651         std::vector<Element *> children = elem->getChildren();
8652         for (unsigned int i=0 ; i<children.size() ; i++)
8653             {
8654             Element *child = children[i];
8655             String tagName = child->getName();
8656             if (tagName == "fileset")
8657                 {
8658                 if (!parseFileSet(child, parent, fileSet))
8659                     return false;
8660                 }
8661             else if (tagName == "libs")
8662                 {
8663                 if (!parent.getValue(child, libsOpt))
8664                     return false;
8665                 libsOpt = strip(libsOpt);
8666                 }
8667             }
8668         return true;
8669         }
8671 private:
8673     FileSet fileSet;
8675     String commandOpt;
8676     String fileNameOpt;
8677     String defFileNameOpt;
8678     String impFileNameOpt;
8679     String libsOpt;
8681 };
8685 /**
8686  * Run the "ar" command to archive .o's into a .a
8687  */
8688 class TaskStaticLib : public Task
8690 public:
8692     TaskStaticLib(MakeBase &par) : Task(par)
8693         { type = TASK_STATICLIB; name = "staticlib"; }
8695     virtual ~TaskStaticLib()
8696         {}
8698     virtual bool execute()
8699         {
8700         String command = parent.eval(commandOpt, "ar crv");
8701         String fileName = parent.eval(fileNameOpt, "");
8703         bool doit = false;
8704         
8705         String fullOut = parent.resolve(fileName);
8706         //trace("ar fullout: %s", fullOut.c_str());
8707         
8708         if (!listFiles(parent, fileSet))
8709             return false;
8710         String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
8711         //trace("###########HERE %s", fileSetDir.c_str());
8713         for (unsigned int i=0 ; i<fileSet.size() ; i++)
8714             {
8715             String fname;
8716             if (fileSetDir.size()>0)
8717                 {
8718                 fname.append(fileSetDir);
8719                 fname.append("/");
8720                 }
8721             fname.append(fileSet[i]);
8722             String fullName = parent.resolve(fname);
8723             //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
8724             if (isNewerThan(fullName, fullOut))
8725                 doit = true;
8726             }
8727         //trace("Needs it:%d", doit);
8728         if (!doit)
8729             {
8730             return true;
8731             }
8733         String cmd = command;
8734         cmd.append(" ");
8735         cmd.append(fullOut);
8736         for (unsigned int i=0 ; i<fileSet.size() ; i++)
8737             {
8738             String fname;
8739             if (fileSetDir.size()>0)
8740                 {
8741                 fname.append(fileSetDir);
8742                 fname.append("/");
8743                 }
8744             fname.append(fileSet[i]);
8745             String fullName = parent.resolve(fname);
8747             cmd.append(" ");
8748             cmd.append(fullName);
8749             }
8751         String outString, errString;
8752         if (!executeCommand(cmd.c_str(), "", outString, errString))
8753             {
8754             error("<staticlib> problem: %s", errString.c_str());
8755             return false;
8756             }
8757         removeFromStatCache(getNativePath(fullOut));
8758         return true;
8759         }
8762     virtual bool parse(Element *elem)
8763         {
8764         if (!parent.getAttribute(elem, "command", commandOpt))
8765             return false;
8766         if (!parent.getAttribute(elem, "file", fileNameOpt))
8767             return false;
8768             
8769         std::vector<Element *> children = elem->getChildren();
8770         for (unsigned int i=0 ; i<children.size() ; i++)
8771             {
8772             Element *child = children[i];
8773             String tagName = child->getName();
8774             if (tagName == "fileset")
8775                 {
8776                 if (!parseFileSet(child, parent, fileSet))
8777                     return false;
8778                 }
8779             }
8780         return true;
8781         }
8783 private:
8785     FileSet fileSet;
8787     String commandOpt;
8788     String fileNameOpt;
8790 };
8795 /**
8796  * Strip an executable
8797  */
8798 class TaskStrip : public Task
8800 public:
8802     TaskStrip(MakeBase &par) : Task(par)
8803         { type = TASK_STRIP; name = "strip"; }
8805     virtual ~TaskStrip()
8806         {}
8808     virtual bool execute()
8809         {
8810         String command     = parent.eval(commandOpt, "strip");
8811         String fileName    = parent.eval(fileNameOpt, "");
8812         String symFileName = parent.eval(symFileNameOpt, "");
8814         String fullName = parent.resolve(fileName);
8815         //trace("fullDir:%s", fullDir.c_str());
8816         String cmd;
8817         String outbuf, errbuf;
8819         if (symFileName.size()>0)
8820             {
8821             String symFullName = parent.resolve(symFileName);
8822             cmd = "objcopy --only-keep-debug ";
8823             cmd.append(getNativePath(fullName));
8824             cmd.append(" ");
8825             cmd.append(getNativePath(symFullName));
8826             if (!executeCommand(cmd, "", outbuf, errbuf))
8827                 {
8828                 error("<strip> symbol file failed : %s", errbuf.c_str());
8829                 return false;
8830                 }
8831             }
8832             
8833         cmd = command;
8834         cmd.append(getNativePath(fullName));
8835         if (!executeCommand(cmd, "", outbuf, errbuf))
8836             {
8837             error("<strip> failed : %s", errbuf.c_str());
8838             return false;
8839             }
8840         removeFromStatCache(getNativePath(fullName));
8841         return true;
8842         }
8844     virtual bool parse(Element *elem)
8845         {
8846         if (!parent.getAttribute(elem, "command", commandOpt))
8847             return false;
8848         if (!parent.getAttribute(elem, "file", fileNameOpt))
8849             return false;
8850         if (!parent.getAttribute(elem, "symfile", symFileNameOpt))
8851             return false;
8852         if (fileNameOpt.size() == 0)
8853             {
8854             error("<strip> requires 'file=\"fileName\"' attribute");
8855             return false;
8856             }
8857         return true;
8858         }
8860 private:
8862     String commandOpt;
8863     String fileNameOpt;
8864     String symFileNameOpt;
8865 };
8868 /**
8869  *
8870  */
8871 class TaskTouch : public Task
8873 public:
8875     TaskTouch(MakeBase &par) : Task(par)
8876         { type = TASK_TOUCH; name = "touch"; }
8878     virtual ~TaskTouch()
8879         {}
8881     virtual bool execute()
8882         {
8883         String fileName = parent.eval(fileNameOpt, "");
8885         String fullName = parent.resolve(fileName);
8886         String nativeFile = getNativePath(fullName);
8887         if (!isRegularFile(fullName) && !isDirectory(fullName))
8888             {            
8889             // S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH
8890             int ret = creat(nativeFile.c_str(), 0666);
8891             if (ret != 0) 
8892                 {
8893                 error("<touch> could not create '%s' : %s",
8894                     nativeFile.c_str(), strerror(ret));
8895                 return false;
8896                 }
8897             return true;
8898             }
8899         int ret = utime(nativeFile.c_str(), (struct utimbuf *)0);
8900         if (ret != 0)
8901             {
8902             error("<touch> could not update the modification time for '%s' : %s",
8903                 nativeFile.c_str(), strerror(ret));
8904             return false;
8905             }
8906         removeFromStatCache(nativeFile);
8907         return true;
8908         }
8910     virtual bool parse(Element *elem)
8911         {
8912         //trace("touch parse");
8913         if (!parent.getAttribute(elem, "file", fileNameOpt))
8914             return false;
8915         if (fileNameOpt.size() == 0)
8916             {
8917             error("<touch> requires 'file=\"fileName\"' attribute");
8918             return false;
8919             }
8920         return true;
8921         }
8923     String fileNameOpt;
8924 };
8927 /**
8928  *
8929  */
8930 class TaskTstamp : public Task
8932 public:
8934     TaskTstamp(MakeBase &par) : Task(par)
8935         { type = TASK_TSTAMP; name = "tstamp"; }
8937     virtual ~TaskTstamp()
8938         {}
8940     virtual bool execute()
8941         {
8942         return true;
8943         }
8945     virtual bool parse(Element *elem)
8946         {
8947         //trace("tstamp parse");
8948         return true;
8949         }
8950 };
8954 /**
8955  *
8956  */
8957 Task *Task::createTask(Element *elem, int lineNr)
8959     String tagName = elem->getName();
8960     //trace("task:%s", tagName.c_str());
8961     Task *task = NULL;
8962     if (tagName == "cc")
8963         task = new TaskCC(parent);
8964     else if (tagName == "copy")
8965         task = new TaskCopy(parent);
8966     else if (tagName == "cxxtestpart")
8967         task = new TaskCxxTestPart(parent);
8968     else if (tagName == "cxxtestroot")
8969         task = new TaskCxxTestRoot(parent);
8970     else if (tagName == "cxxtestrun")
8971         task = new TaskCxxTestRun(parent);
8972     else if (tagName == "delete")
8973         task = new TaskDelete(parent);
8974     else if (tagName == "echo")
8975         task = new TaskEcho(parent);
8976     else if (tagName == "jar")
8977         task = new TaskJar(parent);
8978     else if (tagName == "javac")
8979         task = new TaskJavac(parent);
8980     else if (tagName == "link")
8981         task = new TaskLink(parent);
8982     else if (tagName == "makefile")
8983         task = new TaskMakeFile(parent);
8984     else if (tagName == "mkdir")
8985         task = new TaskMkDir(parent);
8986     else if (tagName == "msgfmt")
8987         task = new TaskMsgFmt(parent);
8988     else if (tagName == "pkg-config")
8989         task = new TaskPkgConfig(parent);
8990     else if (tagName == "ranlib")
8991         task = new TaskRanlib(parent);
8992     else if (tagName == "rc")
8993         task = new TaskRC(parent);
8994     else if (tagName == "sharedlib")
8995         task = new TaskSharedLib(parent);
8996     else if (tagName == "staticlib")
8997         task = new TaskStaticLib(parent);
8998     else if (tagName == "strip")
8999         task = new TaskStrip(parent);
9000     else if (tagName == "touch")
9001         task = new TaskTouch(parent);
9002     else if (tagName == "tstamp")
9003         task = new TaskTstamp(parent);
9004     else
9005         {
9006         error("Unknown task '%s'", tagName.c_str());
9007         return NULL;
9008         }
9010     task->setLine(lineNr);
9012     if (!task->parse(elem))
9013         {
9014         delete task;
9015         return NULL;
9016         }
9017     return task;
9022 //########################################################################
9023 //# T A R G E T
9024 //########################################################################
9026 /**
9027  *
9028  */
9029 class Target : public MakeBase
9032 public:
9034     /**
9035      *
9036      */
9037     Target(Make &par) : parent(par)
9038         { init(); }
9040     /**
9041      *
9042      */
9043     Target(const Target &other) : parent(other.parent)
9044         { init(); assign(other); }
9046     /**
9047      *
9048      */
9049     Target &operator=(const Target &other)
9050         { init(); assign(other); return *this; }
9052     /**
9053      *
9054      */
9055     virtual ~Target()
9056         { cleanup() ; }
9059     /**
9060      *
9061      */
9062     virtual Make &getParent()
9063         { return parent; }
9065     /**
9066      *
9067      */
9068     virtual String getName()
9069         { return name; }
9071     /**
9072      *
9073      */
9074     virtual void setName(const String &val)
9075         { name = val; }
9077     /**
9078      *
9079      */
9080     virtual String getDescription()
9081         { return description; }
9083     /**
9084      *
9085      */
9086     virtual void setDescription(const String &val)
9087         { description = val; }
9089     /**
9090      *
9091      */
9092     virtual void addDependency(const String &val)
9093         { deps.push_back(val); }
9095     /**
9096      *
9097      */
9098     virtual void parseDependencies(const String &val)
9099         { deps = tokenize(val, ", "); }
9101     /**
9102      *
9103      */
9104     virtual std::vector<String> &getDependencies()
9105         { return deps; }
9107     /**
9108      *
9109      */
9110     virtual String getIf()
9111         { return ifVar; }
9113     /**
9114      *
9115      */
9116     virtual void setIf(const String &val)
9117         { ifVar = val; }
9119     /**
9120      *
9121      */
9122     virtual String getUnless()
9123         { return unlessVar; }
9125     /**
9126      *
9127      */
9128     virtual void setUnless(const String &val)
9129         { unlessVar = val; }
9131     /**
9132      *
9133      */
9134     virtual void addTask(Task *val)
9135         { tasks.push_back(val); }
9137     /**
9138      *
9139      */
9140     virtual std::vector<Task *> &getTasks()
9141         { return tasks; }
9143 private:
9145     void init()
9146         {
9147         }
9149     void cleanup()
9150         {
9151         tasks.clear();
9152         }
9154     void assign(const Target &other)
9155         {
9156         //parent      = other.parent;
9157         name        = other.name;
9158         description = other.description;
9159         ifVar       = other.ifVar;
9160         unlessVar   = other.unlessVar;
9161         deps        = other.deps;
9162         tasks       = other.tasks;
9163         }
9165     Make &parent;
9167     String name;
9169     String description;
9171     String ifVar;
9173     String unlessVar;
9175     std::vector<String> deps;
9177     std::vector<Task *> tasks;
9179 };
9188 //########################################################################
9189 //# M A K E
9190 //########################################################################
9193 /**
9194  *
9195  */
9196 class Make : public MakeBase
9199 public:
9201     /**
9202      *
9203      */
9204     Make()
9205         { init(); }
9207     /**
9208      *
9209      */
9210     Make(const Make &other)
9211         { assign(other); }
9213     /**
9214      *
9215      */
9216     Make &operator=(const Make &other)
9217         { assign(other); return *this; }
9219     /**
9220      *
9221      */
9222     virtual ~Make()
9223         { cleanup(); }
9225     /**
9226      *
9227      */
9228     virtual std::map<String, Target> &getTargets()
9229         { return targets; }
9232     /**
9233      *
9234      */
9235     virtual String version()
9236         { return BUILDTOOL_VERSION; }
9238     /**
9239      * Overload a <property>
9240      */
9241     virtual bool specifyProperty(const String &name,
9242                                  const String &value);
9244     /**
9245      *
9246      */
9247     virtual bool run();
9249     /**
9250      *
9251      */
9252     virtual bool run(const String &target);
9256 private:
9258     /**
9259      *
9260      */
9261     void init();
9263     /**
9264      *
9265      */
9266     void cleanup();
9268     /**
9269      *
9270      */
9271     void assign(const Make &other);
9273     /**
9274      *
9275      */
9276     bool executeTask(Task &task);
9279     /**
9280      *
9281      */
9282     bool executeTarget(Target &target,
9283              std::set<String> &targetsCompleted);
9286     /**
9287      *
9288      */
9289     bool execute();
9291     /**
9292      *
9293      */
9294     bool checkTargetDependencies(Target &prop,
9295                     std::vector<String> &depList);
9297     /**
9298      *
9299      */
9300     bool parsePropertyFile(const String &fileName,
9301                            const String &prefix);
9303     /**
9304      *
9305      */
9306     bool parseProperty(Element *elem);
9308     /**
9309      *
9310      */
9311     bool parseFile();
9313     /**
9314      *
9315      */
9316     std::vector<String> glob(const String &pattern);
9319     //###############
9320     //# Fields
9321     //###############
9323     String projectName;
9325     String currentTarget;
9327     String defaultTarget;
9329     String specifiedTarget;
9331     String baseDir;
9333     String description;
9334     
9335     //std::vector<Property> properties;
9336     
9337     std::map<String, Target> targets;
9339     std::vector<Task *> allTasks;
9340     
9341     std::map<String, String> specifiedProperties;
9343 };
9346 //########################################################################
9347 //# C L A S S  M A I N T E N A N C E
9348 //########################################################################
9350 /**
9351  *
9352  */
9353 void Make::init()
9355     uri             = "build.xml";
9356     projectName     = "";
9357     currentTarget   = "";
9358     defaultTarget   = "";
9359     specifiedTarget = "";
9360     baseDir         = "";
9361     description     = "";
9362     envPrefix       = "env.";
9363     pcPrefix        = "pc.";
9364     pccPrefix       = "pcc.";
9365     pclPrefix       = "pcl.";
9366     properties.clear();
9367     for (unsigned int i = 0 ; i < allTasks.size() ; i++)
9368         delete allTasks[i];
9369     allTasks.clear();
9374 /**
9375  *
9376  */
9377 void Make::cleanup()
9379     for (unsigned int i = 0 ; i < allTasks.size() ; i++)
9380         delete allTasks[i];
9381     allTasks.clear();
9386 /**
9387  *
9388  */
9389 void Make::assign(const Make &other)
9391     uri              = other.uri;
9392     projectName      = other.projectName;
9393     currentTarget    = other.currentTarget;
9394     defaultTarget    = other.defaultTarget;
9395     specifiedTarget  = other.specifiedTarget;
9396     baseDir          = other.baseDir;
9397     description      = other.description;
9398     properties       = other.properties;
9403 //########################################################################
9404 //# U T I L I T Y    T A S K S
9405 //########################################################################
9407 /**
9408  *  Perform a file globbing
9409  */
9410 std::vector<String> Make::glob(const String &pattern)
9412     std::vector<String> res;
9413     return res;
9417 //########################################################################
9418 //# P U B L I C    A P I
9419 //########################################################################
9423 /**
9424  *
9425  */
9426 bool Make::executeTarget(Target &target,
9427              std::set<String> &targetsCompleted)
9430     String name = target.getName();
9432     //First get any dependencies for this target
9433     std::vector<String> deps = target.getDependencies();
9434     for (unsigned int i=0 ; i<deps.size() ; i++)
9435         {
9436         String dep = deps[i];
9437         //Did we do it already?  Skip
9438         if (targetsCompleted.find(dep)!=targetsCompleted.end())
9439             continue;
9440             
9441         std::map<String, Target> &tgts =
9442                target.getParent().getTargets();
9443         std::map<String, Target>::iterator iter =
9444                tgts.find(dep);
9445         if (iter == tgts.end())
9446             {
9447             error("Target '%s' dependency '%s' not found",
9448                       name.c_str(),  dep.c_str());
9449             return false;
9450             }
9451         Target depTarget = iter->second;
9452         if (!executeTarget(depTarget, targetsCompleted))
9453             {
9454             return false;
9455             }
9456         }
9458     status("##### Target : %s\n##### %s", name.c_str(),
9459             target.getDescription().c_str());
9461     //Now let's do the tasks
9462     std::vector<Task *> &tasks = target.getTasks();
9463     for (unsigned int i=0 ; i<tasks.size() ; i++)
9464         {
9465         Task *task = tasks[i];
9466         status("--- %s / %s", name.c_str(), task->getName().c_str());
9467         if (!task->execute())
9468             {
9469             return false;
9470             }
9471         }
9472         
9473     targetsCompleted.insert(name);
9474     
9475     return true;
9480 /**
9481  *  Main execute() method.  Start here and work
9482  *  up the dependency tree 
9483  */
9484 bool Make::execute()
9486     status("######## EXECUTE");
9488     //Determine initial target
9489     if (specifiedTarget.size()>0)
9490         {
9491         currentTarget = specifiedTarget;
9492         }
9493     else if (defaultTarget.size()>0)
9494         {
9495         currentTarget = defaultTarget;
9496         }
9497     else
9498         {
9499         error("execute: no specified or default target requested");
9500         return false;
9501         }
9503     std::map<String, Target>::iterator iter =
9504                targets.find(currentTarget);
9505     if (iter == targets.end())
9506         {
9507         error("Initial target '%s' not found",
9508                  currentTarget.c_str());
9509         return false;
9510         }
9511         
9512     //Now run
9513     Target target = iter->second;
9514     std::set<String> targetsCompleted;
9515     if (!executeTarget(target, targetsCompleted))
9516         {
9517         return false;
9518         }
9520     status("######## EXECUTE COMPLETE");
9521     return true;
9527 /**
9528  *
9529  */
9530 bool Make::checkTargetDependencies(Target &target, 
9531                             std::vector<String> &depList)
9533     String tgtName = target.getName().c_str();
9534     depList.push_back(tgtName);
9536     std::vector<String> deps = target.getDependencies();
9537     for (unsigned int i=0 ; i<deps.size() ; i++)
9538         {
9539         String dep = deps[i];
9540         //First thing entered was the starting Target
9541         if (dep == depList[0])
9542             {
9543             error("Circular dependency '%s' found at '%s'",
9544                       dep.c_str(), tgtName.c_str());
9545             std::vector<String>::iterator diter;
9546             for (diter=depList.begin() ; diter!=depList.end() ; diter++)
9547                 {
9548                 error("  %s", diter->c_str());
9549                 }
9550             return false;
9551             }
9553         std::map<String, Target> &tgts =
9554                   target.getParent().getTargets();
9555         std::map<String, Target>::iterator titer = tgts.find(dep);
9556         if (titer == tgts.end())
9557             {
9558             error("Target '%s' dependency '%s' not found",
9559                       tgtName.c_str(), dep.c_str());
9560             return false;
9561             }
9562         if (!checkTargetDependencies(titer->second, depList))
9563             {
9564             return false;
9565             }
9566         }
9567     return true;
9574 static int getword(int pos, const String &inbuf, String &result)
9576     int p = pos;
9577     int len = (int)inbuf.size();
9578     String val;
9579     while (p < len)
9580         {
9581         char ch = inbuf[p];
9582         if (!isalnum(ch) && ch!='.' && ch!='_')
9583             break;
9584         val.push_back(ch);
9585         p++;
9586         }
9587     result = val;
9588     return p;
9594 /**
9595  *
9596  */
9597 bool Make::parsePropertyFile(const String &fileName,
9598                              const String &prefix)
9600     FILE *f = fopen(fileName.c_str(), "r");
9601     if (!f)
9602         {
9603         error("could not open property file %s", fileName.c_str());
9604         return false;
9605         }
9606     int linenr = 0;
9607     while (!feof(f))
9608         {
9609         char buf[256];
9610         if (!fgets(buf, 255, f))
9611             break;
9612         linenr++;
9613         String s = buf;
9614         s = trim(s);
9615         int len = s.size();
9616         if (len == 0)
9617             continue;
9618         if (s[0] == '#')
9619             continue;
9620         String key;
9621         String val;
9622         int p = 0;
9623         int p2 = getword(p, s, key);
9624         if (p2 <= p)
9625             {
9626             error("property file %s, line %d: expected keyword",
9627                     fileName.c_str(), linenr);
9628             return false;
9629             }
9630         if (prefix.size() > 0)
9631             {
9632             key.insert(0, prefix);
9633             }
9635         //skip whitespace
9636         for (p=p2 ; p<len ; p++)
9637             if (!isspace(s[p]))
9638                 break;
9640         if (p>=len || s[p]!='=')
9641             {
9642             error("property file %s, line %d: expected '='",
9643                     fileName.c_str(), linenr);
9644             return false;
9645             }
9646         p++;
9648         //skip whitespace
9649         for ( ; p<len ; p++)
9650             if (!isspace(s[p]))
9651                 break;
9653         /* This way expects a word after the =
9654         p2 = getword(p, s, val);
9655         if (p2 <= p)
9656             {
9657             error("property file %s, line %d: expected value",
9658                     fileName.c_str(), linenr);
9659             return false;
9660             }
9661         */
9662         // This way gets the rest of the line after the =
9663         if (p>=len)
9664             {
9665             error("property file %s, line %d: expected value",
9666                     fileName.c_str(), linenr);
9667             return false;
9668             }
9669         val = s.substr(p);
9670         if (key.size()==0)
9671             continue;
9672         //allow property to be set, even if val=""
9674         //trace("key:'%s' val:'%s'", key.c_str(), val.c_str());
9675         //See if we wanted to overload this property
9676         std::map<String, String>::iterator iter =
9677             specifiedProperties.find(key);
9678         if (iter!=specifiedProperties.end())
9679             {
9680             val = iter->second;
9681             status("overloading property '%s' = '%s'",
9682                    key.c_str(), val.c_str());
9683             }
9684         properties[key] = val;
9685         }
9686     fclose(f);
9687     return true;
9693 /**
9694  *
9695  */
9696 bool Make::parseProperty(Element *elem)
9698     std::vector<Attribute> &attrs = elem->getAttributes();
9699     for (unsigned int i=0 ; i<attrs.size() ; i++)
9700         {
9701         String attrName = attrs[i].getName();
9702         String attrVal  = attrs[i].getValue();
9704         if (attrName == "name")
9705             {
9706             String val;
9707             if (!getAttribute(elem, "value", val))
9708                 return false;
9709             if (val.size() > 0)
9710                 {
9711                 properties[attrVal] = val;
9712                 }
9713             else
9714                 {
9715                 if (!getAttribute(elem, "location", val))
9716                     return false;
9717                 //let the property exist, even if not defined
9718                 properties[attrVal] = val;
9719                 }
9720             //See if we wanted to overload this property
9721             std::map<String, String>::iterator iter =
9722                 specifiedProperties.find(attrVal);
9723             if (iter != specifiedProperties.end())
9724                 {
9725                 val = iter->second;
9726                 status("overloading property '%s' = '%s'",
9727                     attrVal.c_str(), val.c_str());
9728                 properties[attrVal] = val;
9729                 }
9730             }
9731         else if (attrName == "file")
9732             {
9733             String prefix;
9734             if (!getAttribute(elem, "prefix", prefix))
9735                 return false;
9736             if (prefix.size() > 0)
9737                 {
9738                 if (prefix[prefix.size()-1] != '.')
9739                     prefix.push_back('.');
9740                 }
9741             if (!parsePropertyFile(attrName, prefix))
9742                 return false;
9743             }
9744         else if (attrName == "environment")
9745             {
9746             if (attrVal.find('.') != attrVal.npos)
9747                 {
9748                 error("environment prefix cannot have a '.' in it");
9749                 return false;
9750                 }
9751             envPrefix = attrVal;
9752             envPrefix.push_back('.');
9753             }
9754         else if (attrName == "pkg-config")
9755             {
9756             if (attrVal.find('.') != attrVal.npos)
9757                 {
9758                 error("pkg-config prefix cannot have a '.' in it");
9759                 return false;
9760                 }
9761             pcPrefix = attrVal;
9762             pcPrefix.push_back('.');
9763             }
9764         else if (attrName == "pkg-config-cflags")
9765             {
9766             if (attrVal.find('.') != attrVal.npos)
9767                 {
9768                 error("pkg-config-cflags prefix cannot have a '.' in it");
9769                 return false;
9770                 }
9771             pccPrefix = attrVal;
9772             pccPrefix.push_back('.');
9773             }
9774         else if (attrName == "pkg-config-libs")
9775             {
9776             if (attrVal.find('.') != attrVal.npos)
9777                 {
9778                 error("pkg-config-libs prefix cannot have a '.' in it");
9779                 return false;
9780                 }
9781             pclPrefix = attrVal;
9782             pclPrefix.push_back('.');
9783             }
9784         }
9786     return true;
9792 /**
9793  *
9794  */
9795 bool Make::parseFile()
9797     status("######## PARSE : %s", uri.getPath().c_str());
9799     setLine(0);
9801     Parser parser;
9802     Element *root = parser.parseFile(uri.getNativePath());
9803     if (!root)
9804         {
9805         error("Could not open %s for reading",
9806               uri.getNativePath().c_str());
9807         return false;
9808         }
9809     
9810     setLine(root->getLine());
9812     if (root->getChildren().size()==0 ||
9813         root->getChildren()[0]->getName()!="project")
9814         {
9815         error("Main xml element should be <project>");
9816         delete root;
9817         return false;
9818         }
9820     //########## Project attributes
9821     Element *project = root->getChildren()[0];
9822     String s = project->getAttribute("name");
9823     if (s.size() > 0)
9824         projectName = s;
9825     s = project->getAttribute("default");
9826     if (s.size() > 0)
9827         defaultTarget = s;
9828     s = project->getAttribute("basedir");
9829     if (s.size() > 0)
9830         baseDir = s;
9832     //######### PARSE MEMBERS
9833     std::vector<Element *> children = project->getChildren();
9834     for (unsigned int i=0 ; i<children.size() ; i++)
9835         {
9836         Element *elem = children[i];
9837         setLine(elem->getLine());
9838         String tagName = elem->getName();
9840         //########## DESCRIPTION
9841         if (tagName == "description")
9842             {
9843             description = parser.trim(elem->getValue());
9844             }
9846         //######### PROPERTY
9847         else if (tagName == "property")
9848             {
9849             if (!parseProperty(elem))
9850                 return false;
9851             }
9853         //######### TARGET
9854         else if (tagName == "target")
9855             {
9856             String tname   = elem->getAttribute("name");
9857             String tdesc   = elem->getAttribute("description");
9858             String tdeps   = elem->getAttribute("depends");
9859             String tif     = elem->getAttribute("if");
9860             String tunless = elem->getAttribute("unless");
9861             Target target(*this);
9862             target.setName(tname);
9863             target.setDescription(tdesc);
9864             target.parseDependencies(tdeps);
9865             target.setIf(tif);
9866             target.setUnless(tunless);
9867             std::vector<Element *> telems = elem->getChildren();
9868             for (unsigned int i=0 ; i<telems.size() ; i++)
9869                 {
9870                 Element *telem = telems[i];
9871                 Task breeder(*this);
9872                 Task *task = breeder.createTask(telem, telem->getLine());
9873                 if (!task)
9874                     return false;
9875                 allTasks.push_back(task);
9876                 target.addTask(task);
9877                 }
9879             //Check name
9880             if (tname.size() == 0)
9881                 {
9882                 error("no name for target");
9883                 return false;
9884                 }
9885             //Check for duplicate name
9886             if (targets.find(tname) != targets.end())
9887                 {
9888                 error("target '%s' already defined", tname.c_str());
9889                 return false;
9890                 }
9891             //more work than targets[tname]=target, but avoids default allocator
9892             targets.insert(std::make_pair<String, Target>(tname, target));
9893             }
9894         //######### none of the above
9895         else
9896             {
9897             error("unknown toplevel tag: <%s>", tagName.c_str());
9898             return false;
9899             }
9901         }
9903     std::map<String, Target>::iterator iter;
9904     for (iter = targets.begin() ; iter!= targets.end() ; iter++)
9905         {
9906         Target tgt = iter->second;
9907         std::vector<String> depList;
9908         if (!checkTargetDependencies(tgt, depList))
9909             {
9910             return false;
9911             }
9912         }
9915     delete root;
9916     status("######## PARSE COMPLETE");
9917     return true;
9921 /**
9922  * Overload a <property>
9923  */
9924 bool Make::specifyProperty(const String &name, const String &value)
9926     if (specifiedProperties.find(name) != specifiedProperties.end())
9927         {
9928         error("Property %s already specified", name.c_str());
9929         return false;
9930         }
9931     specifiedProperties[name] = value;
9932     return true;
9937 /**
9938  *
9939  */
9940 bool Make::run()
9942     if (!parseFile())
9943         return false;
9944         
9945     if (!execute())
9946         return false;
9948     return true;
9954 /**
9955  * Get a formatted MM:SS.sss time elapsed string
9956  */ 
9957 static String
9958 timeDiffString(struct timeval &x, struct timeval &y)
9960     long microsX  = x.tv_usec;
9961     long secondsX = x.tv_sec;
9962     long microsY  = y.tv_usec;
9963     long secondsY = y.tv_sec;
9964     if (microsX < microsY)
9965         {
9966         microsX += 1000000;
9967         secondsX -= 1;
9968         }
9970     int seconds = (int)(secondsX - secondsY);
9971     int millis  = (int)((microsX - microsY)/1000);
9973     int minutes = seconds/60;
9974     seconds -= minutes*60;
9975     char buf[80];
9976     snprintf(buf, 79, "%dm %d.%03ds", minutes, seconds, millis);
9977     String ret = buf;
9978     return ret;
9979     
9982 /**
9983  *
9984  */
9985 bool Make::run(const String &target)
9987     status("####################################################");
9988     status("#   %s", version().c_str());
9989     status("####################################################");
9990     struct timeval timeStart, timeEnd;
9991     ::gettimeofday(&timeStart, NULL);
9992     specifiedTarget = target;
9993     if (!run())
9994         return false;
9995     ::gettimeofday(&timeEnd, NULL);
9996     String timeStr = timeDiffString(timeEnd, timeStart);
9997     status("####################################################");
9998     status("#   BuildTool Completed : %s", timeStr.c_str());
9999     status("####################################################");
10000     return true;
10009 }// namespace buildtool
10010 //########################################################################
10011 //# M A I N
10012 //########################################################################
10014 typedef buildtool::String String;
10016 /**
10017  *  Format an error message in printf() style
10018  */
10019 static void error(const char *fmt, ...)
10021     va_list ap;
10022     va_start(ap, fmt);
10023     fprintf(stderr, "BuildTool error: ");
10024     vfprintf(stderr, fmt, ap);
10025     fprintf(stderr, "\n");
10026     va_end(ap);
10030 static bool parseProperty(const String &s, String &name, String &val)
10032     int len = s.size();
10033     int i;
10034     for (i=0 ; i<len ; i++)
10035         {
10036         char ch = s[i];
10037         if (ch == '=')
10038             break;
10039         name.push_back(ch);
10040         }
10041     if (i>=len || s[i]!='=')
10042         {
10043         error("property requires -Dname=value");
10044         return false;
10045         }
10046     i++;
10047     for ( ; i<len ; i++)
10048         {
10049         char ch = s[i];
10050         val.push_back(ch);
10051         }
10052     return true;
10056 /**
10057  * Compare a buffer with a key, for the length of the key
10058  */
10059 static bool sequ(const String &buf, const char *key)
10061     int len = buf.size();
10062     for (int i=0 ; key[i] && i<len ; i++)
10063         {
10064         if (key[i] != buf[i])
10065             return false;
10066         }        
10067     return true;
10070 static void usage(int argc, char **argv)
10072     printf("usage:\n");
10073     printf("   %s [options] [target]\n", argv[0]);
10074     printf("Options:\n");
10075     printf("  -help, -h              print this message\n");
10076     printf("  -version               print the version information and exit\n");
10077     printf("  -file <file>           use given buildfile\n");
10078     printf("  -f <file>                 ''\n");
10079     printf("  -D<property>=<value>   use value for given property\n");
10085 /**
10086  * Parse the command-line args, get our options,
10087  * and run this thing
10088  */   
10089 static bool parseOptions(int argc, char **argv)
10091     if (argc < 1)
10092         {
10093         error("Cannot parse arguments");
10094         return false;
10095         }
10097     buildtool::Make make;
10099     String target;
10101     //char *progName = argv[0];
10102     for (int i=1 ; i<argc ; i++)
10103         {
10104         String arg = argv[i];
10105         if (arg.size()>1 && arg[0]=='-')
10106             {
10107             if (arg == "-h" || arg == "-help")
10108                 {
10109                 usage(argc,argv);
10110                 return true;
10111                 }
10112             else if (arg == "-version")
10113                 {
10114                 printf("%s", make.version().c_str());
10115                 return true;
10116                 }
10117             else if (arg == "-f" || arg == "-file")
10118                 {
10119                 if (i>=argc)
10120                    {
10121                    usage(argc, argv);
10122                    return false;
10123                    }
10124                 i++; //eat option
10125                 make.setURI(argv[i]);
10126                 }
10127             else if (arg.size()>2 && sequ(arg, "-D"))
10128                 {
10129                 String s = arg.substr(2, arg.size());
10130                 String name, value;
10131                 if (!parseProperty(s, name, value))
10132                    {
10133                    usage(argc, argv);
10134                    return false;
10135                    }
10136                 if (!make.specifyProperty(name, value))
10137                     return false;
10138                 }
10139             else
10140                 {
10141                 error("Unknown option:%s", arg.c_str());
10142                 return false;
10143                 }
10144             }
10145         else
10146             {
10147             if (target.size()>0)
10148                 {
10149                 error("only one initial target");
10150                 usage(argc, argv);
10151                 return false;
10152                 }
10153             target = arg;
10154             }
10155         }
10157     //We have the options.  Now execute them
10158     if (!make.run(target))
10159         return false;
10161     return true;
10168 static bool runMake()
10170     buildtool::Make make;
10171     if (!make.run())
10172         return false;
10173     return true;
10177 static bool pkgConfigTest()
10179     buildtool::PkgConfig pkgConfig;
10180     if (!pkgConfig.readFile("gtk+-2.0.pc"))
10181         return false;
10182     return true;
10187 static bool depTest()
10189     buildtool::DepTool deptool;
10190     deptool.setSourceDirectory("/dev/ink/inkscape/src");
10191     if (!deptool.generateDependencies("build.dep"))
10192         return false;
10193     std::vector<buildtool::FileRec> res =
10194            deptool.loadDepFile("build.dep");
10195     if (res.size() == 0)
10196         return false;
10197     return true;
10200 static bool popenTest()
10202     buildtool::Make make;
10203     buildtool::String out, err;
10204     bool ret = make.executeCommand("gcc xx.cpp", "", out, err);
10205     printf("Popen test:%d '%s' '%s'\n", ret, out.c_str(), err.c_str());
10206     return true;
10210 static bool propFileTest()
10212     buildtool::Make make;
10213     make.parsePropertyFile("test.prop", "test.");
10214     return true;
10218 int main(int argc, char **argv)
10221     if (!parseOptions(argc, argv))
10222         return 1;
10223     /*
10224     if (!popenTest())
10225         return 1;
10227     if (!depTest())
10228         return 1;
10229     if (!propFileTest())
10230         return 1;
10231     if (runMake())
10232         return 1;
10233     */
10234     return 0;
10238 //########################################################################
10239 //# E N D 
10240 //########################################################################