Code

Node tool: fix snapping of node rotation center
[inkscape.git] / buildtool.cpp
1 /**
2  * Simple build automation tool.
3  *
4  * Authors:
5  *   Bob Jamison
6  *   Jasper van de Gronde
7  *
8  * Copyright (C) 2006-2008 Bob Jamison
9  *
10  *  This library is free software; you can redistribute it and/or
11  *  modify it under the terms of the GNU Lesser General Public
12  *  License as published by the Free Software Foundation; either
13  *  version 2.1 of the License, or (at your option) any later version.
14  *
15  *  This library is distributed in the hope that it will be useful,
16  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  *  Lesser General Public License for more details.
19  *
20  *  You should have received a copy of the GNU Lesser General Public
21  *  License along with this library; if not, write to the Free Software
22  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
23  */
25 /**
26  * To use this file, compile with:
27  * <pre>
28  * g++ -O3 buildtool.cpp -o btool.exe
29  * (or whatever your compiler might be)
30  * Then
31  * btool
32  * or
33  * btool {target}
34  *
35  * Note: if you are using MinGW, and a not very recent version of it,
36  * gettimeofday() might be missing.  If so, just build this file with
37  * this command:
38  * g++ -O3 -DNEED_GETTIMEOFDAY buildtool.cpp -o btool.exe
39  *
40  */
42 #define BUILDTOOL_VERSION  "BuildTool v0.9.9"
44 #include <stdio.h>
45 #include <fcntl.h>
46 #include <unistd.h>
47 #include <stdarg.h>
48 #include <sys/stat.h>
49 #include <time.h>
50 #include <sys/time.h>
51 #include <utime.h>
52 #include <dirent.h>
54 #include <string>
55 #include <map>
56 #include <set>
57 #include <vector>
58 #include <algorithm>
61 #ifdef __WIN32__
62 #include <windows.h>
63 #endif
66 #include <errno.h>
69 //########################################################################
70 //# Definition of gettimeofday() for those who don't have it
71 //########################################################################
72 #ifdef NEED_GETTIMEOFDAY
73 #include <sys/timeb.h>
75 struct timezone {
76       int tz_minuteswest; /* minutes west of Greenwich */
77       int tz_dsttime;     /* type of dst correction */
78     };
80 static int gettimeofday (struct timeval *tv, struct timezone *tz)
81 {
82    struct _timeb tb;
84    if (!tv)
85       return (-1);
87     _ftime (&tb);
88     tv->tv_sec  = tb.time;
89     tv->tv_usec = tb.millitm * 1000 + 500;
90     if (tz)
91         {
92         tz->tz_minuteswest = -60 * _timezone;
93         tz->tz_dsttime = _daylight;
94         }
95     return 0;
96 }
98 #endif
106 namespace buildtool
112 //########################################################################
113 //########################################################################
114 //##  R E G E X P
115 //########################################################################
116 //########################################################################
118 /**
119  * This is the T-Rex regular expression library, which we
120  * gratefully acknowledge.  It's clean code and small size allow
121  * us to embed it in BuildTool without adding a dependency
122  *
123  */    
125 //begin trex.h
127 #ifndef _TREX_H_
128 #define _TREX_H_
129 /***************************************************************
130     T-Rex a tiny regular expression library
132     Copyright (C) 2003-2006 Alberto Demichelis
134     This software is provided 'as-is', without any express 
135     or implied warranty. In no event will the authors be held 
136     liable for any damages arising from the use of this software.
138     Permission is granted to anyone to use this software for 
139     any purpose, including commercial applications, and to alter
140     it and redistribute it freely, subject to the following restrictions:
142         1. The origin of this software must not be misrepresented;
143         you must not claim that you wrote the original software.
144         If you use this software in a product, an acknowledgment
145         in the product documentation would be appreciated but
146         is not required.
148         2. Altered source versions must be plainly marked as such,
149         and must not be misrepresented as being the original software.
151         3. This notice may not be removed or altered from any
152         source distribution.
154 ****************************************************************/
156 #ifdef _UNICODE
157 #define TRexChar unsigned short
158 #define MAX_CHAR 0xFFFF
159 #define _TREXC(c) L##c 
160 #define trex_strlen wcslen
161 #define trex_printf wprintf
162 #else
163 #define TRexChar char
164 #define MAX_CHAR 0xFF
165 #define _TREXC(c) (c) 
166 #define trex_strlen strlen
167 #define trex_printf printf
168 #endif
170 #ifndef TREX_API
171 #define TREX_API extern
172 #endif
174 #define TRex_True 1
175 #define TRex_False 0
177 typedef unsigned int TRexBool;
178 typedef struct TRex TRex;
180 typedef struct {
181     const TRexChar *begin;
182     int len;
183 } TRexMatch;
185 TREX_API TRex *trex_compile(const TRexChar *pattern,const TRexChar **error);
186 TREX_API void trex_free(TRex *exp);
187 TREX_API TRexBool trex_match(TRex* exp,const TRexChar* text);
188 TREX_API TRexBool trex_search(TRex* exp,const TRexChar* text, const TRexChar** out_begin, const TRexChar** out_end);
189 TREX_API TRexBool trex_searchrange(TRex* exp,const TRexChar* text_begin,const TRexChar* text_end,const TRexChar** out_begin, const TRexChar** out_end);
190 TREX_API int trex_getsubexpcount(TRex* exp);
191 TREX_API TRexBool trex_getsubexp(TRex* exp, int n, TRexMatch *subexp);
193 #endif
195 //end trex.h
197 //start trex.c
200 #include <stdio.h>
201 #include <string>
203 /* see copyright notice in trex.h */
204 #include <string.h>
205 #include <stdlib.h>
206 #include <ctype.h>
207 #include <setjmp.h>
208 //#include "trex.h"
210 #ifdef _UNICODE
211 #define scisprint iswprint
212 #define scstrlen wcslen
213 #define scprintf wprintf
214 #define _SC(x) L(x)
215 #else
216 #define scisprint isprint
217 #define scstrlen strlen
218 #define scprintf printf
219 #define _SC(x) (x)
220 #endif
222 #ifdef _DEBUG
223 #include <stdio.h>
225 static const TRexChar *g_nnames[] =
227     _SC("NONE"),_SC("OP_GREEDY"),    _SC("OP_OR"),
228     _SC("OP_EXPR"),_SC("OP_NOCAPEXPR"),_SC("OP_DOT"),    _SC("OP_CLASS"),
229     _SC("OP_CCLASS"),_SC("OP_NCLASS"),_SC("OP_RANGE"),_SC("OP_CHAR"),
230     _SC("OP_EOL"),_SC("OP_BOL"),_SC("OP_WB")
231 };
233 #endif
234 #define OP_GREEDY        (MAX_CHAR+1) // * + ? {n}
235 #define OP_OR            (MAX_CHAR+2)
236 #define OP_EXPR            (MAX_CHAR+3) //parentesis ()
237 #define OP_NOCAPEXPR    (MAX_CHAR+4) //parentesis (?:)
238 #define OP_DOT            (MAX_CHAR+5)
239 #define OP_CLASS        (MAX_CHAR+6)
240 #define OP_CCLASS        (MAX_CHAR+7)
241 #define OP_NCLASS        (MAX_CHAR+8) //negates class the [^
242 #define OP_RANGE        (MAX_CHAR+9)
243 #define OP_CHAR            (MAX_CHAR+10)
244 #define OP_EOL            (MAX_CHAR+11)
245 #define OP_BOL            (MAX_CHAR+12)
246 #define OP_WB            (MAX_CHAR+13)
248 #define TREX_SYMBOL_ANY_CHAR ('.')
249 #define TREX_SYMBOL_GREEDY_ONE_OR_MORE ('+')
250 #define TREX_SYMBOL_GREEDY_ZERO_OR_MORE ('*')
251 #define TREX_SYMBOL_GREEDY_ZERO_OR_ONE ('?')
252 #define TREX_SYMBOL_BRANCH ('|')
253 #define TREX_SYMBOL_END_OF_STRING ('$')
254 #define TREX_SYMBOL_BEGINNING_OF_STRING ('^')
255 #define TREX_SYMBOL_ESCAPE_CHAR ('\\')
258 typedef int TRexNodeType;
260 typedef struct tagTRexNode{
261     TRexNodeType type;
262     int left;
263     int right;
264     int next;
265 }TRexNode;
267 struct TRex{
268     const TRexChar *_eol;
269     const TRexChar *_bol;
270     const TRexChar *_p;
271     int _first;
272     int _op;
273     TRexNode *_nodes;
274     int _nallocated;
275     int _nsize;
276     int _nsubexpr;
277     TRexMatch *_matches;
278     int _currsubexp;
279     void *_jmpbuf;
280     const TRexChar **_error;
281 };
283 static int trex_list(TRex *exp);
285 static int trex_newnode(TRex *exp, TRexNodeType type)
287     TRexNode n;
288     int newid;
289     n.type = type;
290     n.next = n.right = n.left = -1;
291     if(type == OP_EXPR)
292         n.right = exp->_nsubexpr++;
293     if(exp->_nallocated < (exp->_nsize + 1)) {
294         //int oldsize = exp->_nallocated;
295         exp->_nallocated *= 2;
296         exp->_nodes = (TRexNode *)realloc(exp->_nodes, exp->_nallocated * sizeof(TRexNode));
297     }
298     exp->_nodes[exp->_nsize++] = n;
299     newid = exp->_nsize - 1;
300     return (int)newid;
303 static void trex_error(TRex *exp,const TRexChar *error)
305     if(exp->_error) *exp->_error = error;
306     longjmp(*((jmp_buf*)exp->_jmpbuf),-1);
309 static void trex_expect(TRex *exp, int n){
310     if((*exp->_p) != n) 
311         trex_error(exp, _SC("expected paren"));
312     exp->_p++;
315 static TRexChar trex_escapechar(TRex *exp)
317     if(*exp->_p == TREX_SYMBOL_ESCAPE_CHAR){
318         exp->_p++;
319         switch(*exp->_p) {
320         case 'v': exp->_p++; return '\v';
321         case 'n': exp->_p++; return '\n';
322         case 't': exp->_p++; return '\t';
323         case 'r': exp->_p++; return '\r';
324         case 'f': exp->_p++; return '\f';
325         default: return (*exp->_p++);
326         }
327     } else if(!scisprint(*exp->_p)) trex_error(exp,_SC("letter expected"));
328     return (*exp->_p++);
331 static int trex_charclass(TRex *exp,int classid)
333     int n = trex_newnode(exp,OP_CCLASS);
334     exp->_nodes[n].left = classid;
335     return n;
338 static int trex_charnode(TRex *exp,TRexBool isclass)
340     TRexChar t;
341     if(*exp->_p == TREX_SYMBOL_ESCAPE_CHAR) {
342         exp->_p++;
343         switch(*exp->_p) {
344             case 'n': exp->_p++; return trex_newnode(exp,'\n');
345             case 't': exp->_p++; return trex_newnode(exp,'\t');
346             case 'r': exp->_p++; return trex_newnode(exp,'\r');
347             case 'f': exp->_p++; return trex_newnode(exp,'\f');
348             case 'v': exp->_p++; return trex_newnode(exp,'\v');
349             case 'a': case 'A': case 'w': case 'W': case 's': case 'S': 
350             case 'd': case 'D': case 'x': case 'X': case 'c': case 'C': 
351             case 'p': case 'P': case 'l': case 'u': 
352                 {
353                 t = *exp->_p; exp->_p++; 
354                 return trex_charclass(exp,t);
355                 }
356             case 'b': 
357             case 'B':
358                 if(!isclass) {
359                     int node = trex_newnode(exp,OP_WB);
360                     exp->_nodes[node].left = *exp->_p;
361                     exp->_p++; 
362                     return node;
363                 } //else default
364             default: 
365                 t = *exp->_p; exp->_p++; 
366                 return trex_newnode(exp,t);
367         }
368     }
369     else if(!scisprint(*exp->_p)) {
370         
371         trex_error(exp,_SC("letter expected"));
372     }
373     t = *exp->_p; exp->_p++; 
374     return trex_newnode(exp,t);
376 static int trex_class(TRex *exp)
378     int ret = -1;
379     int first = -1,chain;
380     if(*exp->_p == TREX_SYMBOL_BEGINNING_OF_STRING){
381         ret = trex_newnode(exp,OP_NCLASS);
382         exp->_p++;
383     }else ret = trex_newnode(exp,OP_CLASS);
384     
385     if(*exp->_p == ']') trex_error(exp,_SC("empty class"));
386     chain = ret;
387     while(*exp->_p != ']' && exp->_p != exp->_eol) {
388         if(*exp->_p == '-' && first != -1){ 
389             int r,t;
390             if(*exp->_p++ == ']') trex_error(exp,_SC("unfinished range"));
391             r = trex_newnode(exp,OP_RANGE);
392             if(first>*exp->_p) trex_error(exp,_SC("invalid range"));
393             if(exp->_nodes[first].type == OP_CCLASS) trex_error(exp,_SC("cannot use character classes in ranges"));
394             exp->_nodes[r].left = exp->_nodes[first].type;
395             t = trex_escapechar(exp);
396             exp->_nodes[r].right = t;
397             exp->_nodes[chain].next = r;
398             chain = r;
399             first = -1;
400         }
401         else{
402             if(first!=-1){
403                 int c = first;
404                 exp->_nodes[chain].next = c;
405                 chain = c;
406                 first = trex_charnode(exp,TRex_True);
407             }
408             else{
409                 first = trex_charnode(exp,TRex_True);
410             }
411         }
412     }
413     if(first!=-1){
414         int c = first;
415         exp->_nodes[chain].next = c;
416         chain = c;
417         first = -1;
418     }
419     /* hack? */
420     exp->_nodes[ret].left = exp->_nodes[ret].next;
421     exp->_nodes[ret].next = -1;
422     return ret;
425 static int trex_parsenumber(TRex *exp)
427     int ret = *exp->_p-'0';
428     int positions = 10;
429     exp->_p++;
430     while(isdigit(*exp->_p)) {
431         ret = ret*10+(*exp->_p++-'0');
432         if(positions==1000000000) trex_error(exp,_SC("overflow in numeric constant"));
433         positions *= 10;
434     };
435     return ret;
438 static int trex_element(TRex *exp)
440     int ret = -1;
441     switch(*exp->_p)
442     {
443     case '(': {
444         int expr,newn;
445         exp->_p++;
448         if(*exp->_p =='?') {
449             exp->_p++;
450             trex_expect(exp,':');
451             expr = trex_newnode(exp,OP_NOCAPEXPR);
452         }
453         else
454             expr = trex_newnode(exp,OP_EXPR);
455         newn = trex_list(exp);
456         exp->_nodes[expr].left = newn;
457         ret = expr;
458         trex_expect(exp,')');
459               }
460               break;
461     case '[':
462         exp->_p++;
463         ret = trex_class(exp);
464         trex_expect(exp,']');
465         break;
466     case TREX_SYMBOL_END_OF_STRING: exp->_p++; ret = trex_newnode(exp,OP_EOL);break;
467     case TREX_SYMBOL_ANY_CHAR: exp->_p++; ret = trex_newnode(exp,OP_DOT);break;
468     default:
469         ret = trex_charnode(exp,TRex_False);
470         break;
471     }
473     {
474         int op;
475         TRexBool isgreedy = TRex_False;
476         unsigned short p0 = 0, p1 = 0;
477         switch(*exp->_p){
478             case TREX_SYMBOL_GREEDY_ZERO_OR_MORE: p0 = 0; p1 = 0xFFFF; exp->_p++; isgreedy = TRex_True; break;
479             case TREX_SYMBOL_GREEDY_ONE_OR_MORE: p0 = 1; p1 = 0xFFFF; exp->_p++; isgreedy = TRex_True; break;
480             case TREX_SYMBOL_GREEDY_ZERO_OR_ONE: p0 = 0; p1 = 1; exp->_p++; isgreedy = TRex_True; break;
481             case '{':
482                 exp->_p++;
483                 if(!isdigit(*exp->_p)) trex_error(exp,_SC("number expected"));
484                 p0 = (unsigned short)trex_parsenumber(exp);
485                 /*******************************/
486                 switch(*exp->_p) {
487             case '}':
488                 p1 = p0; exp->_p++;
489                 break;
490             case ',':
491                 exp->_p++;
492                 p1 = 0xFFFF;
493                 if(isdigit(*exp->_p)){
494                     p1 = (unsigned short)trex_parsenumber(exp);
495                 }
496                 trex_expect(exp,'}');
497                 break;
498             default:
499                 trex_error(exp,_SC(", or } expected"));
500         }
501         /*******************************/
502         isgreedy = TRex_True; 
503         break;
505         }
506         if(isgreedy) {
507             int nnode = trex_newnode(exp,OP_GREEDY);
508             op = OP_GREEDY;
509             exp->_nodes[nnode].left = ret;
510             exp->_nodes[nnode].right = ((p0)<<16)|p1;
511             ret = nnode;
512         }
513     }
514     if((*exp->_p != TREX_SYMBOL_BRANCH) && (*exp->_p != ')') && (*exp->_p != TREX_SYMBOL_GREEDY_ZERO_OR_MORE) && (*exp->_p != TREX_SYMBOL_GREEDY_ONE_OR_MORE) && (*exp->_p != '\0')) {
515         int nnode = trex_element(exp);
516         exp->_nodes[ret].next = nnode;
517     }
519     return ret;
522 static int trex_list(TRex *exp)
524     int ret=-1,e;
525     if(*exp->_p == TREX_SYMBOL_BEGINNING_OF_STRING) {
526         exp->_p++;
527         ret = trex_newnode(exp,OP_BOL);
528     }
529     e = trex_element(exp);
530     if(ret != -1) {
531         exp->_nodes[ret].next = e;
532     }
533     else ret = e;
535     if(*exp->_p == TREX_SYMBOL_BRANCH) {
536         int temp,tright;
537         exp->_p++;
538         temp = trex_newnode(exp,OP_OR);
539         exp->_nodes[temp].left = ret;
540         tright = trex_list(exp);
541         exp->_nodes[temp].right = tright;
542         ret = temp;
543     }
544     return ret;
547 static TRexBool trex_matchcclass(int cclass,TRexChar c)
549     switch(cclass) {
550     case 'a': return isalpha(c)?TRex_True:TRex_False;
551     case 'A': return !isalpha(c)?TRex_True:TRex_False;
552     case 'w': return (isalnum(c) || c == '_')?TRex_True:TRex_False;
553     case 'W': return (!isalnum(c) && c != '_')?TRex_True:TRex_False;
554     case 's': return isspace(c)?TRex_True:TRex_False;
555     case 'S': return !isspace(c)?TRex_True:TRex_False;
556     case 'd': return isdigit(c)?TRex_True:TRex_False;
557     case 'D': return !isdigit(c)?TRex_True:TRex_False;
558     case 'x': return isxdigit(c)?TRex_True:TRex_False;
559     case 'X': return !isxdigit(c)?TRex_True:TRex_False;
560     case 'c': return iscntrl(c)?TRex_True:TRex_False;
561     case 'C': return !iscntrl(c)?TRex_True:TRex_False;
562     case 'p': return ispunct(c)?TRex_True:TRex_False;
563     case 'P': return !ispunct(c)?TRex_True:TRex_False;
564     case 'l': return islower(c)?TRex_True:TRex_False;
565     case 'u': return isupper(c)?TRex_True:TRex_False;
566     }
567     return TRex_False; /*cannot happen*/
570 static TRexBool trex_matchclass(TRex* exp,TRexNode *node,TRexChar c)
572     do {
573         switch(node->type) {
574             case OP_RANGE:
575                 if(c >= node->left && c <= node->right) return TRex_True;
576                 break;
577             case OP_CCLASS:
578                 if(trex_matchcclass(node->left,c)) return TRex_True;
579                 break;
580             default:
581                 if(c == node->type)return TRex_True;
582         }
583     } while((node->next != -1) && (node = &exp->_nodes[node->next]));
584     return TRex_False;
587 static const TRexChar *trex_matchnode(TRex* exp,TRexNode *node,const TRexChar *str,TRexNode *next)
589     
590     TRexNodeType type = node->type;
591     switch(type) {
592     case OP_GREEDY: {
593         //TRexNode *greedystop = (node->next != -1) ? &exp->_nodes[node->next] : NULL;
594         TRexNode *greedystop = NULL;
595         int p0 = (node->right >> 16)&0x0000FFFF, p1 = node->right&0x0000FFFF, nmaches = 0;
596         const TRexChar *s=str, *good = str;
598         if(node->next != -1) {
599             greedystop = &exp->_nodes[node->next];
600         }
601         else {
602             greedystop = next;
603         }
605         while((nmaches == 0xFFFF || nmaches < p1)) {
607             const TRexChar *stop;
608             if(!(s = trex_matchnode(exp,&exp->_nodes[node->left],s,greedystop)))
609                 break;
610             nmaches++;
611             good=s;
612             if(greedystop) {
613                 //checks that 0 matches satisfy the expression(if so skips)
614                 //if not would always stop(for instance if is a '?')
615                 if(greedystop->type != OP_GREEDY ||
616                 (greedystop->type == OP_GREEDY && ((greedystop->right >> 16)&0x0000FFFF) != 0))
617                 {
618                     TRexNode *gnext = NULL;
619                     if(greedystop->next != -1) {
620                         gnext = &exp->_nodes[greedystop->next];
621                     }else if(next && next->next != -1){
622                         gnext = &exp->_nodes[next->next];
623                     }
624                     stop = trex_matchnode(exp,greedystop,s,gnext);
625                     if(stop) {
626                         //if satisfied stop it
627                         if(p0 == p1 && p0 == nmaches) break;
628                         else if(nmaches >= p0 && p1 == 0xFFFF) break;
629                         else if(nmaches >= p0 && nmaches <= p1) break;
630                     }
631                 }
632             }
633             
634             if(s >= exp->_eol)
635                 break;
636         }
637         if(p0 == p1 && p0 == nmaches) return good;
638         else if(nmaches >= p0 && p1 == 0xFFFF) return good;
639         else if(nmaches >= p0 && nmaches <= p1) return good;
640         return NULL;
641     }
642     case OP_OR: {
643             const TRexChar *asd = str;
644             TRexNode *temp=&exp->_nodes[node->left];
645             while( (asd = trex_matchnode(exp,temp,asd,NULL)) ) {
646                 if(temp->next != -1)
647                     temp = &exp->_nodes[temp->next];
648                 else
649                     return asd;
650             }
651             asd = str;
652             temp = &exp->_nodes[node->right];
653             while( (asd = trex_matchnode(exp,temp,asd,NULL)) ) {
654                 if(temp->next != -1)
655                     temp = &exp->_nodes[temp->next];
656                 else
657                     return asd;
658             }
659             return NULL;
660             break;
661     }
662     case OP_EXPR:
663     case OP_NOCAPEXPR:{
664             TRexNode *n = &exp->_nodes[node->left];
665             const TRexChar *cur = str;
666             int capture = -1;
667             if(node->type != OP_NOCAPEXPR && node->right == exp->_currsubexp) {
668                 capture = exp->_currsubexp;
669                 exp->_matches[capture].begin = cur;
670                 exp->_currsubexp++;
671             }
672             
673             do {
674                 TRexNode *subnext = NULL;
675                 if(n->next != -1) {
676                     subnext = &exp->_nodes[n->next];
677                 }else {
678                     subnext = next;
679                 }
680                 if(!(cur = trex_matchnode(exp,n,cur,subnext))) {
681                     if(capture != -1){
682                         exp->_matches[capture].begin = 0;
683                         exp->_matches[capture].len = 0;
684                     }
685                     return NULL;
686                 }
687             } while((n->next != -1) && (n = &exp->_nodes[n->next]));
689             if(capture != -1) 
690                 exp->_matches[capture].len = cur - exp->_matches[capture].begin;
691             return cur;
692     }                 
693     case OP_WB:
694         if((str == exp->_bol && !isspace(*str))
695          || (str == exp->_eol && !isspace(*(str-1)))
696          || (!isspace(*str) && isspace(*(str+1)))
697          || (isspace(*str) && !isspace(*(str+1))) ) {
698             return (node->left == 'b')?str:NULL;
699         }
700         return (node->left == 'b')?NULL:str;
701     case OP_BOL:
702         if(str == exp->_bol) return str;
703         return NULL;
704     case OP_EOL:
705         if(str == exp->_eol) return str;
706         return NULL;
707     case OP_DOT:{
708         *str++;
709                 }
710         return str;
711     case OP_NCLASS:
712     case OP_CLASS:
713         if(trex_matchclass(exp,&exp->_nodes[node->left],*str)?(type == OP_CLASS?TRex_True:TRex_False):(type == OP_NCLASS?TRex_True:TRex_False)) {
714             *str++;
715             return str;
716         }
717         return NULL;
718     case OP_CCLASS:
719         if(trex_matchcclass(node->left,*str)) {
720             *str++;
721             return str;
722         }
723         return NULL;
724     default: /* char */
725         if(*str != node->type) return NULL;
726         *str++;
727         return str;
728     }
729     return NULL;
732 /* public api */
733 TRex *trex_compile(const TRexChar *pattern,const TRexChar **error)
735     TRex *exp = (TRex *)malloc(sizeof(TRex));
736     exp->_eol = exp->_bol = NULL;
737     exp->_p = pattern;
738     exp->_nallocated = (int)scstrlen(pattern) * sizeof(TRexChar);
739     exp->_nodes = (TRexNode *)malloc(exp->_nallocated * sizeof(TRexNode));
740     exp->_nsize = 0;
741     exp->_matches = 0;
742     exp->_nsubexpr = 0;
743     exp->_first = trex_newnode(exp,OP_EXPR);
744     exp->_error = error;
745     exp->_jmpbuf = malloc(sizeof(jmp_buf));
746     if(setjmp(*((jmp_buf*)exp->_jmpbuf)) == 0) {
747         int res = trex_list(exp);
748         exp->_nodes[exp->_first].left = res;
749         if(*exp->_p!='\0')
750             trex_error(exp,_SC("unexpected character"));
751 #ifdef _DEBUG
752         {
753             int nsize,i;
754             TRexNode *t;
755             nsize = exp->_nsize;
756             t = &exp->_nodes[0];
757             scprintf(_SC("\n"));
758             for(i = 0;i < nsize; i++) {
759                 if(exp->_nodes[i].type>MAX_CHAR)
760                     scprintf(_SC("[%02d] %10s "),i,g_nnames[exp->_nodes[i].type-MAX_CHAR]);
761                 else
762                     scprintf(_SC("[%02d] %10c "),i,exp->_nodes[i].type);
763                 scprintf(_SC("left %02d right %02d next %02d\n"),exp->_nodes[i].left,exp->_nodes[i].right,exp->_nodes[i].next);
764             }
765             scprintf(_SC("\n"));
766         }
767 #endif
768         exp->_matches = (TRexMatch *) malloc(exp->_nsubexpr * sizeof(TRexMatch));
769         memset(exp->_matches,0,exp->_nsubexpr * sizeof(TRexMatch));
770     }
771     else{
772         trex_free(exp);
773         return NULL;
774     }
775     return exp;
778 void trex_free(TRex *exp)
780     if(exp)    {
781         if(exp->_nodes) free(exp->_nodes);
782         if(exp->_jmpbuf) free(exp->_jmpbuf);
783         if(exp->_matches) free(exp->_matches);
784         free(exp);
785     }
788 TRexBool trex_match(TRex* exp,const TRexChar* text)
790     const TRexChar* res = NULL;
791     exp->_bol = text;
792     exp->_eol = text + scstrlen(text);
793     exp->_currsubexp = 0;
794     res = trex_matchnode(exp,exp->_nodes,text,NULL);
795     if(res == NULL || res != exp->_eol)
796         return TRex_False;
797     return TRex_True;
800 TRexBool trex_searchrange(TRex* exp,const TRexChar* text_begin,const TRexChar* text_end,const TRexChar** out_begin, const TRexChar** out_end)
802     const TRexChar *cur = NULL;
803     int node = exp->_first;
804     if(text_begin >= text_end) return TRex_False;
805     exp->_bol = text_begin;
806     exp->_eol = text_end;
807     do {
808         cur = text_begin;
809         while(node != -1) {
810             exp->_currsubexp = 0;
811             cur = trex_matchnode(exp,&exp->_nodes[node],cur,NULL);
812             if(!cur)
813                 break;
814             node = exp->_nodes[node].next;
815         }
816         *text_begin++;
817     } while(cur == NULL && text_begin != text_end);
819     if(cur == NULL)
820         return TRex_False;
822     --text_begin;
824     if(out_begin) *out_begin = text_begin;
825     if(out_end) *out_end = cur;
826     return TRex_True;
829 TRexBool trex_search(TRex* exp,const TRexChar* text, const TRexChar** out_begin, const TRexChar** out_end)
831     return trex_searchrange(exp,text,text + scstrlen(text),out_begin,out_end);
834 int trex_getsubexpcount(TRex* exp)
836     return exp->_nsubexpr;
839 TRexBool trex_getsubexp(TRex* exp, int n, TRexMatch *subexp)
841     if( n<0 || n >= exp->_nsubexpr) return TRex_False;
842     *subexp = exp->_matches[n];
843     return TRex_True;
847 //########################################################################
848 //########################################################################
849 //##  E N D    R E G E X P
850 //########################################################################
851 //########################################################################
857 //########################################################################
858 //########################################################################
859 //##  X M L
860 //########################################################################
861 //########################################################################
863 // Note:  This mini-dom library comes from Pedro, another little project
864 // of mine.
866 typedef std::string String;
867 typedef unsigned int XMLCh;
870 class Namespace
872 public:
873     Namespace()
874         {}
876     Namespace(const String &prefixArg, const String &namespaceURIArg)
877         {
878         prefix       = prefixArg;
879         namespaceURI = namespaceURIArg;
880         }
882     Namespace(const Namespace &other)
883         {
884         assign(other);
885         }
887     Namespace &operator=(const Namespace &other)
888         {
889         assign(other);
890         return *this;
891         }
893     virtual ~Namespace()
894         {}
896     virtual String getPrefix()
897         { return prefix; }
899     virtual String getNamespaceURI()
900         { return namespaceURI; }
902 protected:
904     void assign(const Namespace &other)
905         {
906         prefix       = other.prefix;
907         namespaceURI = other.namespaceURI;
908         }
910     String prefix;
911     String namespaceURI;
913 };
915 class Attribute
917 public:
918     Attribute()
919         {}
921     Attribute(const String &nameArg, const String &valueArg)
922         {
923         name  = nameArg;
924         value = valueArg;
925         }
927     Attribute(const Attribute &other)
928         {
929         assign(other);
930         }
932     Attribute &operator=(const Attribute &other)
933         {
934         assign(other);
935         return *this;
936         }
938     virtual ~Attribute()
939         {}
941     virtual String getName()
942         { return name; }
944     virtual String getValue()
945         { return value; }
947 protected:
949     void assign(const Attribute &other)
950         {
951         name  = other.name;
952         value = other.value;
953         }
955     String name;
956     String value;
958 };
961 class Element
963 friend class Parser;
965 public:
966     Element()
967         {
968         init();
969         }
971     Element(const String &nameArg)
972         {
973         init();
974         name   = nameArg;
975         }
977     Element(const String &nameArg, const String &valueArg)
978         {
979         init();
980         name   = nameArg;
981         value  = valueArg;
982         }
984     Element(const Element &other)
985         {
986         assign(other);
987         }
989     Element &operator=(const Element &other)
990         {
991         assign(other);
992         return *this;
993         }
995     virtual Element *clone();
997     virtual ~Element()
998         {
999         for (unsigned int i=0 ; i<children.size() ; i++)
1000             delete children[i];
1001         }
1003     virtual String getName()
1004         { return name; }
1006     virtual String getValue()
1007         { return value; }
1009     Element *getParent()
1010         { return parent; }
1012     std::vector<Element *> getChildren()
1013         { return children; }
1015     std::vector<Element *> findElements(const String &name);
1017     String getAttribute(const String &name);
1019     std::vector<Attribute> &getAttributes()
1020         { return attributes; } 
1022     String getTagAttribute(const String &tagName, const String &attrName);
1024     String getTagValue(const String &tagName);
1026     void addChild(Element *child);
1028     void addAttribute(const String &name, const String &value);
1030     void addNamespace(const String &prefix, const String &namespaceURI);
1033     /**
1034      * Prettyprint an XML tree to an output stream.  Elements are indented
1035      * according to element hierarchy.
1036      * @param f a stream to receive the output
1037      * @param elem the element to output
1038      */
1039     void writeIndented(FILE *f);
1041     /**
1042      * Prettyprint an XML tree to standard output.  This is the equivalent of
1043      * writeIndented(stdout).
1044      * @param elem the element to output
1045      */
1046     void print();
1047     
1048     int getLine()
1049         { return line; }
1051 protected:
1053     void init()
1054         {
1055         parent = NULL;
1056         line   = 0;
1057         }
1059     void assign(const Element &other)
1060         {
1061         parent     = other.parent;
1062         children   = other.children;
1063         attributes = other.attributes;
1064         namespaces = other.namespaces;
1065         name       = other.name;
1066         value      = other.value;
1067         line       = other.line;
1068         }
1070     void findElementsRecursive(std::vector<Element *>&res, const String &name);
1072     void writeIndentedRecursive(FILE *f, int indent);
1074     Element *parent;
1076     std::vector<Element *>children;
1078     std::vector<Attribute> attributes;
1079     std::vector<Namespace> namespaces;
1081     String name;
1082     String value;
1083     
1084     int line;
1085 };
1091 class Parser
1093 public:
1094     /**
1095      * Constructor
1096      */
1097     Parser()
1098         { init(); }
1100     virtual ~Parser()
1101         {}
1103     /**
1104      * Parse XML in a char buffer.
1105      * @param buf a character buffer to parse
1106      * @param pos position to start parsing
1107      * @param len number of chars, from pos, to parse.
1108      * @return a pointer to the root of the XML document;
1109      */
1110     Element *parse(const char *buf,int pos,int len);
1112     /**
1113      * Parse XML in a char buffer.
1114      * @param buf a character buffer to parse
1115      * @param pos position to start parsing
1116      * @param len number of chars, from pos, to parse.
1117      * @return a pointer to the root of the XML document;
1118      */
1119     Element *parse(const String &buf);
1121     /**
1122      * Parse a named XML file.  The file is loaded like a data file;
1123      * the original format is not preserved.
1124      * @param fileName the name of the file to read
1125      * @return a pointer to the root of the XML document;
1126      */
1127     Element *parseFile(const String &fileName);
1129     /**
1130      * Utility method to preprocess a string for XML
1131      * output, escaping its entities.
1132      * @param str the string to encode
1133      */
1134     static String encode(const String &str);
1136     /**
1137      *  Removes whitespace from beginning and end of a string
1138      */
1139     String trim(const String &s);
1141 private:
1143     void init()
1144         {
1145         keepGoing       = true;
1146         currentNode     = NULL;
1147         parselen        = 0;
1148         parsebuf        = NULL;
1149         currentPosition = 0;
1150         }
1152     int countLines(int begin, int end);
1154     void getLineAndColumn(int pos, int *lineNr, int *colNr);
1156     void error(const char *fmt, ...);
1158     int peek(int pos);
1160     int match(int pos, const char *text);
1162     int skipwhite(int p);
1164     int getWord(int p0, String &buf);
1166     int getQuoted(int p0, String &buf, int do_i_parse);
1168     int parseVersion(int p0);
1170     int parseDoctype(int p0);
1172     int parseElement(int p0, Element *par,int depth);
1174     Element *parse(XMLCh *buf,int pos,int len);
1176     bool       keepGoing;
1177     Element    *currentNode;
1178     int        parselen;
1179     XMLCh      *parsebuf;
1180     String     cdatabuf;
1181     int        currentPosition;
1182 };
1187 //########################################################################
1188 //# E L E M E N T
1189 //########################################################################
1191 Element *Element::clone()
1193     Element *elem = new Element(name, value);
1194     elem->parent     = parent;
1195     elem->attributes = attributes;
1196     elem->namespaces = namespaces;
1197     elem->line       = line;
1199     std::vector<Element *>::iterator iter;
1200     for (iter = children.begin(); iter != children.end() ; iter++)
1201         {
1202         elem->addChild((*iter)->clone());
1203         }
1204     return elem;
1208 void Element::findElementsRecursive(std::vector<Element *>&res, const String &name)
1210     if (getName() == name)
1211         {
1212         res.push_back(this);
1213         }
1214     for (unsigned int i=0; i<children.size() ; i++)
1215         children[i]->findElementsRecursive(res, name);
1218 std::vector<Element *> Element::findElements(const String &name)
1220     std::vector<Element *> res;
1221     findElementsRecursive(res, name);
1222     return res;
1225 String Element::getAttribute(const String &name)
1227     for (unsigned int i=0 ; i<attributes.size() ; i++)
1228         if (attributes[i].getName() ==name)
1229             return attributes[i].getValue();
1230     return "";
1233 String Element::getTagAttribute(const String &tagName, const String &attrName)
1235     std::vector<Element *>elems = findElements(tagName);
1236     if (elems.size() <1)
1237         return "";
1238     String res = elems[0]->getAttribute(attrName);
1239     return res;
1242 String Element::getTagValue(const String &tagName)
1244     std::vector<Element *>elems = findElements(tagName);
1245     if (elems.size() <1)
1246         return "";
1247     String res = elems[0]->getValue();
1248     return res;
1251 void Element::addChild(Element *child)
1253     if (!child)
1254         return;
1255     child->parent = this;
1256     children.push_back(child);
1260 void Element::addAttribute(const String &name, const String &value)
1262     Attribute attr(name, value);
1263     attributes.push_back(attr);
1266 void Element::addNamespace(const String &prefix, const String &namespaceURI)
1268     Namespace ns(prefix, namespaceURI);
1269     namespaces.push_back(ns);
1272 void Element::writeIndentedRecursive(FILE *f, int indent)
1274     int i;
1275     if (!f)
1276         return;
1277     //Opening tag, and attributes
1278     for (i=0;i<indent;i++)
1279         fputc(' ',f);
1280     fprintf(f,"<%s",name.c_str());
1281     for (unsigned int i=0 ; i<attributes.size() ; i++)
1282         {
1283         fprintf(f," %s=\"%s\"",
1284               attributes[i].getName().c_str(),
1285               attributes[i].getValue().c_str());
1286         }
1287     for (unsigned int i=0 ; i<namespaces.size() ; i++)
1288         {
1289         fprintf(f," xmlns:%s=\"%s\"",
1290               namespaces[i].getPrefix().c_str(),
1291               namespaces[i].getNamespaceURI().c_str());
1292         }
1293     fprintf(f,">\n");
1295     //Between the tags
1296     if (value.size() > 0)
1297         {
1298         for (int i=0;i<indent;i++)
1299             fputc(' ', f);
1300         fprintf(f," %s\n", value.c_str());
1301         }
1303     for (unsigned int i=0 ; i<children.size() ; i++)
1304         children[i]->writeIndentedRecursive(f, indent+2);
1306     //Closing tag
1307     for (int i=0; i<indent; i++)
1308         fputc(' ',f);
1309     fprintf(f,"</%s>\n", name.c_str());
1312 void Element::writeIndented(FILE *f)
1314     writeIndentedRecursive(f, 0);
1317 void Element::print()
1319     writeIndented(stdout);
1323 //########################################################################
1324 //# P A R S E R
1325 //########################################################################
1329 typedef struct
1330     {
1331     const char *escaped;
1332     char value;
1333     } EntityEntry;
1335 static EntityEntry entities[] =
1337     { "&amp;" , '&'  },
1338     { "&lt;"  , '<'  },
1339     { "&gt;"  , '>'  },
1340     { "&apos;", '\'' },
1341     { "&quot;", '"'  },
1342     { NULL    , '\0' }
1343 };
1347 /**
1348  *  Removes whitespace from beginning and end of a string
1349  */
1350 String Parser::trim(const String &s)
1352     if (s.size() < 1)
1353         return s;
1354     
1355     //Find first non-ws char
1356     unsigned int begin = 0;
1357     for ( ; begin < s.size() ; begin++)
1358         {
1359         if (!isspace(s[begin]))
1360             break;
1361         }
1363     //Find first non-ws char, going in reverse
1364     unsigned int end = s.size() - 1;
1365     for ( ; end > begin ; end--)
1366         {
1367         if (!isspace(s[end]))
1368             break;
1369         }
1370     //trace("begin:%d  end:%d", begin, end);
1372     String res = s.substr(begin, end-begin+1);
1373     return res;
1377 int Parser::countLines(int begin, int end)
1379     int count = 0;
1380     for (int i=begin ; i<end ; i++)
1381         {
1382         XMLCh ch = parsebuf[i];
1383         if (ch == '\n' || ch == '\r')
1384             count++;
1385         }
1386     return count;
1390 void Parser::getLineAndColumn(int pos, int *lineNr, int *colNr)
1392     int line = 1;
1393     int col  = 1;
1394     for (long i=0 ; i<pos ; i++)
1395         {
1396         XMLCh ch = parsebuf[i];
1397         if (ch == '\n' || ch == '\r')
1398             {
1399             col = 0;
1400             line ++;
1401             }
1402         else
1403             col++;
1404         }
1405     *lineNr = line;
1406     *colNr  = col;
1411 void Parser::error(const char *fmt, ...)
1413     int lineNr;
1414     int colNr;
1415     getLineAndColumn(currentPosition, &lineNr, &colNr);
1416     va_list args;
1417     fprintf(stderr, "xml error at line %d, column %d:", lineNr, colNr);
1418     va_start(args,fmt);
1419     vfprintf(stderr,fmt,args);
1420     va_end(args) ;
1421     fprintf(stderr, "\n");
1426 int Parser::peek(int pos)
1428     if (pos >= parselen)
1429         return -1;
1430     currentPosition = pos;
1431     int ch = parsebuf[pos];
1432     //printf("ch:%c\n", ch);
1433     return ch;
1438 String Parser::encode(const String &str)
1440     String ret;
1441     for (unsigned int i=0 ; i<str.size() ; i++)
1442         {
1443         XMLCh ch = (XMLCh)str[i];
1444         if (ch == '&')
1445             ret.append("&amp;");
1446         else if (ch == '<')
1447             ret.append("&lt;");
1448         else if (ch == '>')
1449             ret.append("&gt;");
1450         else if (ch == '\'')
1451             ret.append("&apos;");
1452         else if (ch == '"')
1453             ret.append("&quot;");
1454         else
1455             ret.push_back(ch);
1457         }
1458     return ret;
1462 int Parser::match(int p0, const char *text)
1464     int p = p0;
1465     while (*text)
1466         {
1467         if (peek(p) != *text)
1468             return p0;
1469         p++; text++;
1470         }
1471     return p;
1476 int Parser::skipwhite(int p)
1479     while (p<parselen)
1480         {
1481         int p2 = match(p, "<!--");
1482         if (p2 > p)
1483             {
1484             p = p2;
1485             while (p<parselen)
1486               {
1487               p2 = match(p, "-->");
1488               if (p2 > p)
1489                   {
1490                   p = p2;
1491                   break;
1492                   }
1493               p++;
1494               }
1495           }
1496       XMLCh b = peek(p);
1497       if (!isspace(b))
1498           break;
1499       p++;
1500       }
1501   return p;
1504 /* modify this to allow all chars for an element or attribute name*/
1505 int Parser::getWord(int p0, String &buf)
1507     int p = p0;
1508     while (p<parselen)
1509         {
1510         XMLCh b = peek(p);
1511         if (b<=' ' || b=='/' || b=='>' || b=='=')
1512             break;
1513         buf.push_back(b);
1514         p++;
1515         }
1516     return p;
1519 int Parser::getQuoted(int p0, String &buf, int do_i_parse)
1522     int p = p0;
1523     if (peek(p) != '"' && peek(p) != '\'')
1524         return p0;
1525     p++;
1527     while ( p<parselen )
1528         {
1529         XMLCh b = peek(p);
1530         if (b=='"' || b=='\'')
1531             break;
1532         if (b=='&' && do_i_parse)
1533             {
1534             bool found = false;
1535             for (EntityEntry *ee = entities ; ee->value ; ee++)
1536                 {
1537                 int p2 = match(p, ee->escaped);
1538                 if (p2>p)
1539                     {
1540                     buf.push_back(ee->value);
1541                     p = p2;
1542                     found = true;
1543                     break;
1544                     }
1545                 }
1546             if (!found)
1547                 {
1548                 error("unterminated entity");
1549                 return false;
1550                 }
1551             }
1552         else
1553             {
1554             buf.push_back(b);
1555             p++;
1556             }
1557         }
1558     return p;
1561 int Parser::parseVersion(int p0)
1563     //printf("### parseVersion: %d\n", p0);
1565     int p = p0;
1567     p = skipwhite(p0);
1569     if (peek(p) != '<')
1570         return p0;
1572     p++;
1573     if (p>=parselen || peek(p)!='?')
1574         return p0;
1576     p++;
1578     String buf;
1580     while (p<parselen)
1581         {
1582         XMLCh ch = peek(p);
1583         if (ch=='?')
1584             {
1585             p++;
1586             break;
1587             }
1588         buf.push_back(ch);
1589         p++;
1590         }
1592     if (peek(p) != '>')
1593         return p0;
1594     p++;
1596     //printf("Got version:%s\n",buf.c_str());
1597     return p;
1600 int Parser::parseDoctype(int p0)
1602     //printf("### parseDoctype: %d\n", p0);
1604     int p = p0;
1605     p = skipwhite(p);
1607     if (p>=parselen || peek(p)!='<')
1608         return p0;
1610     p++;
1612     if (peek(p)!='!' || peek(p+1)=='-')
1613         return p0;
1614     p++;
1616     String buf;
1617     while (p<parselen)
1618         {
1619         XMLCh ch = peek(p);
1620         if (ch=='>')
1621             {
1622             p++;
1623             break;
1624             }
1625         buf.push_back(ch);
1626         p++;
1627         }
1629     //printf("Got doctype:%s\n",buf.c_str());
1630     return p;
1635 int Parser::parseElement(int p0, Element *par,int lineNr)
1638     int p = p0;
1640     int p2 = p;
1642     p = skipwhite(p);
1644     //## Get open tag
1645     XMLCh ch = peek(p);
1646     if (ch!='<')
1647         return p0;
1649     //int line, col;
1650     //getLineAndColumn(p, &line, &col);
1652     p++;
1654     String openTagName;
1655     p = skipwhite(p);
1656     p = getWord(p, openTagName);
1657     //printf("####tag :%s\n", openTagName.c_str());
1658     p = skipwhite(p);
1660     //Add element to tree
1661     Element *n = new Element(openTagName);
1662     n->line = lineNr + countLines(p0, p);
1663     n->parent = par;
1664     par->addChild(n);
1666     // Get attributes
1667     if (peek(p) != '>')
1668         {
1669         while (p<parselen)
1670             {
1671             p = skipwhite(p);
1672             ch = peek(p);
1673             //printf("ch:%c\n",ch);
1674             if (ch=='>')
1675                 break;
1676             else if (ch=='/' && p<parselen+1)
1677                 {
1678                 p++;
1679                 p = skipwhite(p);
1680                 ch = peek(p);
1681                 if (ch=='>')
1682                     {
1683                     p++;
1684                     //printf("quick close\n");
1685                     return p;
1686                     }
1687                 }
1688             String attrName;
1689             p2 = getWord(p, attrName);
1690             if (p2==p)
1691                 break;
1692             //printf("name:%s",buf);
1693             p=p2;
1694             p = skipwhite(p);
1695             ch = peek(p);
1696             //printf("ch:%c\n",ch);
1697             if (ch!='=')
1698                 break;
1699             p++;
1700             p = skipwhite(p);
1701             // ch = parsebuf[p];
1702             // printf("ch:%c\n",ch);
1703             String attrVal;
1704             p2 = getQuoted(p, attrVal, true);
1705             p=p2+1;
1706             //printf("name:'%s'   value:'%s'\n",attrName.c_str(),attrVal.c_str());
1707             char *namestr = (char *)attrName.c_str();
1708             if (strncmp(namestr, "xmlns:", 6)==0)
1709                 n->addNamespace(attrName, attrVal);
1710             else
1711                 n->addAttribute(attrName, attrVal);
1712             }
1713         }
1715     bool cdata = false;
1717     p++;
1718     // ### Get intervening data ### */
1719     String data;
1720     while (p<parselen)
1721         {
1722         //# COMMENT
1723         p2 = match(p, "<!--");
1724         if (!cdata && p2>p)
1725             {
1726             p = p2;
1727             while (p<parselen)
1728                 {
1729                 p2 = match(p, "-->");
1730                 if (p2 > p)
1731                     {
1732                     p = p2;
1733                     break;
1734                     }
1735                 p++;
1736                 }
1737             }
1739         ch = peek(p);
1740         //# END TAG
1741         if (ch=='<' && !cdata && peek(p+1)=='/')
1742             {
1743             break;
1744             }
1745         //# CDATA
1746         p2 = match(p, "<![CDATA[");
1747         if (p2 > p)
1748             {
1749             cdata = true;
1750             p = p2;
1751             continue;
1752             }
1754         //# CHILD ELEMENT
1755         if (ch == '<')
1756             {
1757             p2 = parseElement(p, n, lineNr + countLines(p0, p));
1758             if (p2 == p)
1759                 {
1760                 /*
1761                 printf("problem on element:%s.  p2:%d p:%d\n",
1762                       openTagName.c_str(), p2, p);
1763                 */
1764                 return p0;
1765                 }
1766             p = p2;
1767             continue;
1768             }
1769         //# ENTITY
1770         if (ch=='&' && !cdata)
1771             {
1772             bool found = false;
1773             for (EntityEntry *ee = entities ; ee->value ; ee++)
1774                 {
1775                 int p2 = match(p, ee->escaped);
1776                 if (p2>p)
1777                     {
1778                     data.push_back(ee->value);
1779                     p = p2;
1780                     found = true;
1781                     break;
1782                     }
1783                 }
1784             if (!found)
1785                 {
1786                 error("unterminated entity");
1787                 return -1;
1788                 }
1789             continue;
1790             }
1792         //# NONE OF THE ABOVE
1793         data.push_back(ch);
1794         p++;
1795         }/*while*/
1798     n->value = data;
1799     //printf("%d : data:%s\n",p,data.c_str());
1801     //## Get close tag
1802     p = skipwhite(p);
1803     ch = peek(p);
1804     if (ch != '<')
1805         {
1806         error("no < for end tag\n");
1807         return p0;
1808         }
1809     p++;
1810     ch = peek(p);
1811     if (ch != '/')
1812         {
1813         error("no / on end tag");
1814         return p0;
1815         }
1816     p++;
1817     ch = peek(p);
1818     p = skipwhite(p);
1819     String closeTagName;
1820     p = getWord(p, closeTagName);
1821     if (openTagName != closeTagName)
1822         {
1823         error("Mismatched closing tag.  Expected </%S>. Got '%S'.",
1824                 openTagName.c_str(), closeTagName.c_str());
1825         return p0;
1826         }
1827     p = skipwhite(p);
1828     if (peek(p) != '>')
1829         {
1830         error("no > on end tag for '%s'", closeTagName.c_str());
1831         return p0;
1832         }
1833     p++;
1834     // printf("close element:%s\n",closeTagName.c_str());
1835     p = skipwhite(p);
1836     return p;
1842 Element *Parser::parse(XMLCh *buf,int pos,int len)
1844     parselen = len;
1845     parsebuf = buf;
1846     Element *rootNode = new Element("root");
1847     pos = parseVersion(pos);
1848     pos = parseDoctype(pos);
1849     pos = parseElement(pos, rootNode, 1);
1850     return rootNode;
1854 Element *Parser::parse(const char *buf, int pos, int len)
1856     XMLCh *charbuf = new XMLCh[len + 1];
1857     long i = 0;
1858     for ( ; i < len ; i++)
1859         charbuf[i] = (XMLCh)buf[i];
1860     charbuf[i] = '\0';
1862     Element *n = parse(charbuf, pos, len);
1863     delete[] charbuf;
1864     return n;
1867 Element *Parser::parse(const String &buf)
1869     long len = (long)buf.size();
1870     XMLCh *charbuf = new XMLCh[len + 1];
1871     long i = 0;
1872     for ( ; i < len ; i++)
1873         charbuf[i] = (XMLCh)buf[i];
1874     charbuf[i] = '\0';
1876     Element *n = parse(charbuf, 0, len);
1877     delete[] charbuf;
1878     return n;
1881 Element *Parser::parseFile(const String &fileName)
1884     //##### LOAD INTO A CHAR BUF, THEN CONVERT TO XMLCh
1885     FILE *f = fopen(fileName.c_str(), "rb");
1886     if (!f)
1887         return NULL;
1889     struct stat  statBuf;
1890     if (fstat(fileno(f),&statBuf)<0)
1891         {
1892         fclose(f);
1893         return NULL;
1894         }
1895     long filelen = statBuf.st_size;
1897     //printf("length:%d\n",filelen);
1898     XMLCh *charbuf = new XMLCh[filelen + 1];
1899     for (XMLCh *p=charbuf ; !feof(f) ; p++)
1900         {
1901         *p = (XMLCh)fgetc(f);
1902         }
1903     fclose(f);
1904     charbuf[filelen] = '\0';
1907     /*
1908     printf("nrbytes:%d\n",wc_count);
1909     printf("buf:%ls\n======\n",charbuf);
1910     */
1911     Element *n = parse(charbuf, 0, filelen);
1912     delete[] charbuf;
1913     return n;
1916 //########################################################################
1917 //########################################################################
1918 //##  E N D    X M L
1919 //########################################################################
1920 //########################################################################
1927 //########################################################################
1928 //########################################################################
1929 //##  U R I
1930 //########################################################################
1931 //########################################################################
1933 //This would normally be a call to a UNICODE function
1934 #define isLetter(x) isalpha(x)
1936 /**
1937  *  A class that implements the W3C URI resource reference.
1938  */
1939 class URI
1941 public:
1943     typedef enum
1944         {
1945         SCHEME_NONE =0,
1946         SCHEME_DATA,
1947         SCHEME_HTTP,
1948         SCHEME_HTTPS,
1949         SCHEME_FTP,
1950         SCHEME_FILE,
1951         SCHEME_LDAP,
1952         SCHEME_MAILTO,
1953         SCHEME_NEWS,
1954         SCHEME_TELNET
1955         } SchemeTypes;
1957     /**
1958      *
1959      */
1960     URI()
1961         {
1962         init();
1963         }
1965     /**
1966      *
1967      */
1968     URI(const String &str)
1969         {
1970         init();
1971         parse(str);
1972         }
1975     /**
1976      *
1977      */
1978     URI(const char *str)
1979         {
1980         init();
1981         String domStr = str;
1982         parse(domStr);
1983         }
1986     /**
1987      *
1988      */
1989     URI(const URI &other)
1990         {
1991         init();
1992         assign(other);
1993         }
1996     /**
1997      *
1998      */
1999     URI &operator=(const URI &other)
2000         {
2001         init();
2002         assign(other);
2003         return *this;
2004         }
2007     /**
2008      *
2009      */
2010     virtual ~URI()
2011         {}
2015     /**
2016      *
2017      */
2018     virtual bool parse(const String &str);
2020     /**
2021      *
2022      */
2023     virtual String toString() const;
2025     /**
2026      *
2027      */
2028     virtual int getScheme() const;
2030     /**
2031      *
2032      */
2033     virtual String getSchemeStr() const;
2035     /**
2036      *
2037      */
2038     virtual String getAuthority() const;
2040     /**
2041      *  Same as getAuthority, but if the port has been specified
2042      *  as host:port , the port will not be included
2043      */
2044     virtual String getHost() const;
2046     /**
2047      *
2048      */
2049     virtual int getPort() const;
2051     /**
2052      *
2053      */
2054     virtual String getPath() const;
2056     /**
2057      *
2058      */
2059     virtual String getNativePath() const;
2061     /**
2062      *
2063      */
2064     virtual bool isAbsolute() const;
2066     /**
2067      *
2068      */
2069     virtual bool isOpaque() const;
2071     /**
2072      *
2073      */
2074     virtual String getQuery() const;
2076     /**
2077      *
2078      */
2079     virtual String getFragment() const;
2081     /**
2082      *
2083      */
2084     virtual URI resolve(const URI &other) const;
2086     /**
2087      *
2088      */
2089     virtual void normalize();
2091 private:
2093     /**
2094      *
2095      */
2096     void init()
2097         {
2098         parsebuf  = NULL;
2099         parselen  = 0;
2100         scheme    = SCHEME_NONE;
2101         schemeStr = "";
2102         port      = 0;
2103         authority = "";
2104         path      = "";
2105         absolute  = false;
2106         opaque    = false;
2107         query     = "";
2108         fragment  = "";
2109         }
2112     /**
2113      *
2114      */
2115     void assign(const URI &other)
2116         {
2117         scheme    = other.scheme;
2118         schemeStr = other.schemeStr;
2119         authority = other.authority;
2120         port      = other.port;
2121         path      = other.path;
2122         absolute  = other.absolute;
2123         opaque    = other.opaque;
2124         query     = other.query;
2125         fragment  = other.fragment;
2126         }
2128     int scheme;
2130     String schemeStr;
2132     String authority;
2134     bool portSpecified;
2136     int port;
2138     String path;
2140     bool absolute;
2142     bool opaque;
2144     String query;
2146     String fragment;
2148     void error(const char *fmt, ...);
2150     void trace(const char *fmt, ...);
2153     int peek(int p);
2155     int match(int p, const char *key);
2157     int parseScheme(int p);
2159     int parseHierarchicalPart(int p0);
2161     int parseQuery(int p0);
2163     int parseFragment(int p0);
2165     int parse(int p);
2167     char *parsebuf;
2169     int parselen;
2171 };
2175 typedef struct
2177     int         ival;
2178     const char *sval;
2179     int         port;
2180 } LookupEntry;
2182 LookupEntry schemes[] =
2184     { URI::SCHEME_DATA,   "data:",    0 },
2185     { URI::SCHEME_HTTP,   "http:",   80 },
2186     { URI::SCHEME_HTTPS,  "https:", 443 },
2187     { URI::SCHEME_FTP,    "ftp",     12 },
2188     { URI::SCHEME_FILE,   "file:",    0 },
2189     { URI::SCHEME_LDAP,   "ldap:",  123 },
2190     { URI::SCHEME_MAILTO, "mailto:", 25 },
2191     { URI::SCHEME_NEWS,   "news:",  117 },
2192     { URI::SCHEME_TELNET, "telnet:", 23 },
2193     { 0,                  NULL,       0 }
2194 };
2197 String URI::toString() const
2199     String str = schemeStr;
2200     if (authority.size() > 0)
2201         {
2202         str.append("//");
2203         str.append(authority);
2204         }
2205     str.append(path);
2206     if (query.size() > 0)
2207         {
2208         str.append("?");
2209         str.append(query);
2210         }
2211     if (fragment.size() > 0)
2212         {
2213         str.append("#");
2214         str.append(fragment);
2215         }
2216     return str;
2220 int URI::getScheme() const
2222     return scheme;
2225 String URI::getSchemeStr() const
2227     return schemeStr;
2231 String URI::getAuthority() const
2233     String ret = authority;
2234     if (portSpecified && port>=0)
2235         {
2236         char buf[7];
2237         snprintf(buf, 6, ":%6d", port);
2238         ret.append(buf);
2239         }
2240     return ret;
2243 String URI::getHost() const
2245     return authority;
2248 int URI::getPort() const
2250     return port;
2254 String URI::getPath() const
2256     return path;
2259 String URI::getNativePath() const
2261     String npath;
2262 #ifdef __WIN32__
2263     unsigned int firstChar = 0;
2264     if (path.size() >= 3)
2265         {
2266         if (path[0] == '/' &&
2267             isLetter(path[1]) &&
2268             path[2] == ':')
2269             firstChar++;
2270          }
2271     for (unsigned int i=firstChar ; i<path.size() ; i++)
2272         {
2273         XMLCh ch = (XMLCh) path[i];
2274         if (ch == '/')
2275             npath.push_back((XMLCh)'\\');
2276         else
2277             npath.push_back(ch);
2278         }
2279 #else
2280     npath = path;
2281 #endif
2282     return npath;
2286 bool URI::isAbsolute() const
2288     return absolute;
2291 bool URI::isOpaque() const
2293     return opaque;
2297 String URI::getQuery() const
2299     return query;
2303 String URI::getFragment() const
2305     return fragment;
2309 URI URI::resolve(const URI &other) const
2311     //### According to w3c, this is handled in 3 cases
2313     //## 1
2314     if (opaque || other.isAbsolute())
2315         return other;
2317     //## 2
2318     if (other.fragment.size()  >  0 &&
2319         other.path.size()      == 0 &&
2320         other.scheme           == SCHEME_NONE &&
2321         other.authority.size() == 0 &&
2322         other.query.size()     == 0 )
2323         {
2324         URI fragUri = *this;
2325         fragUri.fragment = other.fragment;
2326         return fragUri;
2327         }
2329     //## 3 http://www.ietf.org/rfc/rfc2396.txt, section 5.2
2330     URI newUri;
2331     //# 3.1
2332     newUri.scheme    = scheme;
2333     newUri.schemeStr = schemeStr;
2334     newUri.query     = other.query;
2335     newUri.fragment  = other.fragment;
2336     if (other.authority.size() > 0)
2337         {
2338         //# 3.2
2339         if (absolute || other.absolute)
2340             newUri.absolute = true;
2341         newUri.authority = other.authority;
2342         newUri.port      = other.port;//part of authority
2343         newUri.path      = other.path;
2344         }
2345     else
2346         {
2347         //# 3.3
2348         if (other.absolute)
2349             {
2350             newUri.absolute = true;
2351             newUri.path     = other.path;
2352             }
2353         else
2354             {
2355             unsigned int pos = path.find_last_of('/');
2356             if (pos != path.npos)
2357                 {
2358                 String tpath = path.substr(0, pos+1);
2359                 tpath.append(other.path);
2360                 newUri.path = tpath;
2361                 }
2362             else
2363                 newUri.path = other.path;
2364             }
2365         }
2367     newUri.normalize();
2368     return newUri;
2373 /**
2374  *  This follows the Java URI algorithm:
2375  *   1. All "." segments are removed.
2376  *   2. If a ".." segment is preceded by a non-".." segment
2377  *          then both of these segments are removed. This step
2378  *          is repeated until it is no longer applicable.
2379  *   3. If the path is relative, and if its first segment
2380  *          contains a colon character (':'), then a "." segment
2381  *          is prepended. This prevents a relative URI with a path
2382  *          such as "a:b/c/d" from later being re-parsed as an
2383  *          opaque URI with a scheme of "a" and a scheme-specific
2384  *          part of "b/c/d". (Deviation from RFC 2396)
2385  */
2386 void URI::normalize()
2388     std::vector<String> segments;
2390     //## Collect segments
2391     if (path.size()<2)
2392         return;
2393     bool abs = false;
2394     unsigned int pos=0;
2395     if (path[0]=='/')
2396         {
2397         abs = true;
2398         pos++;
2399         }
2400     while (pos < path.size())
2401         {
2402         unsigned int pos2 = path.find('/', pos);
2403         if (pos2==path.npos)
2404             {
2405             String seg = path.substr(pos);
2406             //printf("last segment:%s\n", seg.c_str());
2407             segments.push_back(seg);
2408             break;
2409             }
2410         if (pos2>pos)
2411             {
2412             String seg = path.substr(pos, pos2-pos);
2413             //printf("segment:%s\n", seg.c_str());
2414             segments.push_back(seg);
2415             }
2416         pos = pos2;
2417         pos++;
2418         }
2420     //## Clean up (normalize) segments
2421     bool edited = false;
2422     std::vector<String>::iterator iter;
2423     for (iter=segments.begin() ; iter!=segments.end() ; )
2424         {
2425         String s = *iter;
2426         if (s == ".")
2427             {
2428             iter = segments.erase(iter);
2429             edited = true;
2430             }
2431         else if (s == ".." &&
2432                  iter != segments.begin() &&
2433                  *(iter-1) != "..")
2434             {
2435             iter--; //back up, then erase two entries
2436             iter = segments.erase(iter);
2437             iter = segments.erase(iter);
2438             edited = true;
2439             }
2440         else
2441             iter++;
2442         }
2444     //## Rebuild path, if necessary
2445     if (edited)
2446         {
2447         path.clear();
2448         if (abs)
2449             {
2450             path.append("/");
2451             }
2452         std::vector<String>::iterator iter;
2453         for (iter=segments.begin() ; iter!=segments.end() ; iter++)
2454             {
2455             if (iter != segments.begin())
2456                 path.append("/");
2457             path.append(*iter);
2458             }
2459         }
2465 //#########################################################################
2466 //# M E S S A G E S
2467 //#########################################################################
2469 void URI::error(const char *fmt, ...)
2471     va_list args;
2472     fprintf(stderr, "URI error: ");
2473     va_start(args, fmt);
2474     vfprintf(stderr, fmt, args);
2475     va_end(args);
2476     fprintf(stderr, "\n");
2479 void URI::trace(const char *fmt, ...)
2481     va_list args;
2482     fprintf(stdout, "URI: ");
2483     va_start(args, fmt);
2484     vfprintf(stdout, fmt, args);
2485     va_end(args);
2486     fprintf(stdout, "\n");
2492 //#########################################################################
2493 //# P A R S I N G
2494 //#########################################################################
2498 int URI::peek(int p)
2500     if (p<0 || p>=parselen)
2501         return -1;
2502     return parsebuf[p];
2507 int URI::match(int p0, const char *key)
2509     int p = p0;
2510     while (p < parselen)
2511         {
2512         if (*key == '\0')
2513             return p;
2514         else if (*key != parsebuf[p])
2515             break;
2516         p++; key++;
2517         }
2518     return p0;
2521 //#########################################################################
2522 //#  Parsing is performed according to:
2523 //#  http://www.gbiv.com/protocols/uri/rfc/rfc3986.html#components
2524 //#########################################################################
2526 int URI::parseScheme(int p0)
2528     int p = p0;
2529     for (LookupEntry *entry = schemes; entry->sval ; entry++)
2530         {
2531         int p2 = match(p, entry->sval);
2532         if (p2 > p)
2533             {
2534             schemeStr = entry->sval;
2535             scheme    = entry->ival;
2536             port      = entry->port;
2537             p = p2;
2538             return p;
2539             }
2540         }
2542     return p;
2546 int URI::parseHierarchicalPart(int p0)
2548     int p = p0;
2549     int ch;
2551     //# Authority field (host and port, for example)
2552     int p2 = match(p, "//");
2553     if (p2 > p)
2554         {
2555         p = p2;
2556         portSpecified = false;
2557         String portStr;
2558         while (p < parselen)
2559             {
2560             ch = peek(p);
2561             if (ch == '/')
2562                 break;
2563             else if (ch == ':')
2564                 portSpecified = true;
2565             else if (portSpecified)
2566                 portStr.push_back((XMLCh)ch);
2567             else
2568                 authority.push_back((XMLCh)ch);
2569             p++;
2570             }
2571         if (portStr.size() > 0)
2572             {
2573             char *pstr = (char *)portStr.c_str();
2574             char *endStr;
2575             long val = strtol(pstr, &endStr, 10);
2576             if (endStr > pstr) //successful parse?
2577                 port = val;
2578             }
2579         }
2581     //# Are we absolute?
2582     ch = peek(p);
2583     if (isLetter(ch) && peek(p+1)==':')
2584         {
2585         absolute = true;
2586         path.push_back((XMLCh)'/');
2587         }
2588     else if (ch == '/')
2589         {
2590         absolute = true;
2591         if (p>p0) //in other words, if '/' is not the first char
2592             opaque = true;
2593         path.push_back((XMLCh)ch);
2594         p++;
2595         }
2597     while (p < parselen)
2598         {
2599         ch = peek(p);
2600         if (ch == '?' || ch == '#')
2601             break;
2602         path.push_back((XMLCh)ch);
2603         p++;
2604         }
2606     return p;
2609 int URI::parseQuery(int p0)
2611     int p = p0;
2612     int ch = peek(p);
2613     if (ch != '?')
2614         return p0;
2616     p++;
2617     while (p < parselen)
2618         {
2619         ch = peek(p);
2620         if (ch == '#')
2621             break;
2622         query.push_back((XMLCh)ch);
2623         p++;
2624         }
2627     return p;
2630 int URI::parseFragment(int p0)
2633     int p = p0;
2634     int ch = peek(p);
2635     if (ch != '#')
2636         return p0;
2638     p++;
2639     while (p < parselen)
2640         {
2641         ch = peek(p);
2642         if (ch == '?')
2643             break;
2644         fragment.push_back((XMLCh)ch);
2645         p++;
2646         }
2649     return p;
2653 int URI::parse(int p0)
2656     int p = p0;
2658     int p2 = parseScheme(p);
2659     if (p2 < 0)
2660         {
2661         error("Scheme");
2662         return -1;
2663         }
2664     p = p2;
2667     p2 = parseHierarchicalPart(p);
2668     if (p2 < 0)
2669         {
2670         error("Hierarchical part");
2671         return -1;
2672         }
2673     p = p2;
2675     p2 = parseQuery(p);
2676     if (p2 < 0)
2677         {
2678         error("Query");
2679         return -1;
2680         }
2681     p = p2;
2684     p2 = parseFragment(p);
2685     if (p2 < 0)
2686         {
2687         error("Fragment");
2688         return -1;
2689         }
2690     p = p2;
2692     return p;
2698 bool URI::parse(const String &str)
2700     init();
2701     
2702     parselen = str.size();
2704     String tmp;
2705     for (unsigned int i=0 ; i<str.size() ; i++)
2706         {
2707         XMLCh ch = (XMLCh) str[i];
2708         if (ch == '\\')
2709             tmp.push_back((XMLCh)'/');
2710         else
2711             tmp.push_back(ch);
2712         }
2713     parsebuf = (char *) tmp.c_str();
2716     int p = parse(0);
2717     normalize();
2719     if (p < 0)
2720         {
2721         error("Syntax error");
2722         return false;
2723         }
2725     //printf("uri:%s\n", toString().c_str());
2726     //printf("path:%s\n", path.c_str());
2728     return true;
2739 //########################################################################
2740 //########################################################################
2741 //##  M A K E
2742 //########################################################################
2743 //########################################################################
2745 //########################################################################
2746 //# Stat cache to speed up stat requests
2747 //########################################################################
2748 struct StatResult {
2749     int result;
2750     struct stat statInfo;
2751 };
2752 typedef std::map<String, StatResult> statCacheType;
2753 static statCacheType statCache;
2754 static int cachedStat(const String &f, struct stat *s) {
2755     //printf("Stat path: %s\n", f.c_str());
2756     std::pair<statCacheType::iterator, bool> result = statCache.insert(statCacheType::value_type(f, StatResult()));
2757     if (result.second) {
2758         result.first->second.result = stat(f.c_str(), &(result.first->second.statInfo));
2759     }
2760     *s = result.first->second.statInfo;
2761     return result.first->second.result;
2763 static void removeFromStatCache(const String f) {
2764     //printf("Removing from cache: %s\n", f.c_str());
2765     statCache.erase(f);
2768 //########################################################################
2769 //# Dir cache to speed up dir requests
2770 //########################################################################
2771 /*struct DirListing {
2772     bool available;
2773     std::vector<String> files;
2774     std::vector<String> dirs;
2775 };
2776 typedef std::map<String, DirListing > dirCacheType;
2777 static dirCacheType dirCache;
2778 static const DirListing &cachedDir(String fullDir)
2780     String dirNative = getNativePath(fullDir);
2781     std::pair<dirCacheType::iterator,bool> result = dirCache.insert(dirCacheType::value_type(dirNative, DirListing()));
2782     if (result.second) {
2783         DIR *dir = opendir(dirNative.c_str());
2784         if (!dir)
2785             {
2786             error("Could not open directory %s : %s",
2787                 dirNative.c_str(), strerror(errno));
2788             result.first->second.available = false;
2789             }
2790         else
2791             {
2792             result.first->second.available = true;
2793             while (true)
2794                 {
2795                 struct dirent *de = readdir(dir);
2796                 if (!de)
2797                     break;
2799                 //Get the directory member name
2800                 String s = de->d_name;
2801                 if (s.size() == 0 || s[0] == '.')
2802                     continue;
2803                 String childName;
2804                 if (dirName.size()>0)
2805                     {
2806                     childName.append(dirName);
2807                     childName.append("/");
2808                     }
2809                 childName.append(s);
2810                 String fullChild = baseDir;
2811                 fullChild.append("/");
2812                 fullChild.append(childName);
2813                 
2814                 if (isDirectory(fullChild))
2815                     {
2816                     //trace("directory: %s", childName.c_str());
2817                     if (!listFiles(baseDir, childName, res))
2818                         return false;
2819                     continue;
2820                     }
2821                 else if (!isRegularFile(fullChild))
2822                     {
2823                     error("unknown file:%s", childName.c_str());
2824                     return false;
2825                     }
2827             //all done!
2828                 res.push_back(childName);
2830                 }
2831             closedir(dir);
2832             }
2833     }
2834     return result.first->second;
2835 }*/
2837 //########################################################################
2838 //# F I L E S E T
2839 //########################################################################
2840 /**
2841  * This is the descriptor for a <fileset> item
2842  */
2843 class FileSet
2845 public:
2847     /**
2848      *
2849      */
2850     FileSet()
2851         {}
2853     /**
2854      *
2855      */
2856     FileSet(const FileSet &other)
2857         { assign(other); }
2859     /**
2860      *
2861      */
2862     FileSet &operator=(const FileSet &other)
2863         { assign(other); return *this; }
2865     /**
2866      *
2867      */
2868     virtual ~FileSet()
2869         {}
2871     /**
2872      *
2873      */
2874     String getDirectory() const
2875         { return directory; }
2876         
2877     /**
2878      *
2879      */
2880     void setDirectory(const String &val)
2881         { directory = val; }
2883     /**
2884      *
2885      */
2886     void setFiles(const std::vector<String> &val)
2887         { files = val; }
2889     /**
2890      *
2891      */
2892     std::vector<String> getFiles() const
2893         { return files; }
2894         
2895     /**
2896      *
2897      */
2898     void setIncludes(const std::vector<String> &val)
2899         { includes = val; }
2901     /**
2902      *
2903      */
2904     std::vector<String> getIncludes() const
2905         { return includes; }
2906         
2907     /**
2908      *
2909      */
2910     void setExcludes(const std::vector<String> &val)
2911         { excludes = val; }
2913     /**
2914      *
2915      */
2916     std::vector<String> getExcludes() const
2917         { return excludes; }
2918         
2919     /**
2920      *
2921      */
2922     unsigned int size() const
2923         { return files.size(); }
2924         
2925     /**
2926      *
2927      */
2928     String operator[](int index) const
2929         { return files[index]; }
2930         
2931     /**
2932      *
2933      */
2934     void clear()
2935         {
2936         directory = "";
2937         files.clear();
2938         includes.clear();
2939         excludes.clear();
2940         }
2941         
2943 private:
2945     void assign(const FileSet &other)
2946         {
2947         directory = other.directory;
2948         files     = other.files;
2949         includes  = other.includes;
2950         excludes  = other.excludes;
2951         }
2953     String directory;
2954     std::vector<String> files;
2955     std::vector<String> includes;
2956     std::vector<String> excludes;
2957 };
2960 //########################################################################
2961 //# F I L E L I S T
2962 //########################################################################
2963 /**
2964  * This is a simpler, explicitly-named list of files
2965  */
2966 class FileList
2968 public:
2970     /**
2971      *
2972      */
2973     FileList()
2974         {}
2976     /**
2977      *
2978      */
2979     FileList(const FileList &other)
2980         { assign(other); }
2982     /**
2983      *
2984      */
2985     FileList &operator=(const FileList &other)
2986         { assign(other); return *this; }
2988     /**
2989      *
2990      */
2991     virtual ~FileList()
2992         {}
2994     /**
2995      *
2996      */
2997     String getDirectory()
2998         { return directory; }
2999         
3000     /**
3001      *
3002      */
3003     void setDirectory(const String &val)
3004         { directory = val; }
3006     /**
3007      *
3008      */
3009     void setFiles(const std::vector<String> &val)
3010         { files = val; }
3012     /**
3013      *
3014      */
3015     std::vector<String> getFiles()
3016         { return files; }
3017         
3018     /**
3019      *
3020      */
3021     unsigned int size()
3022         { return files.size(); }
3023         
3024     /**
3025      *
3026      */
3027     String operator[](int index)
3028         { return files[index]; }
3029         
3030     /**
3031      *
3032      */
3033     void clear()
3034         {
3035         directory = "";
3036         files.clear();
3037         }
3038         
3040 private:
3042     void assign(const FileList &other)
3043         {
3044         directory = other.directory;
3045         files     = other.files;
3046         }
3048     String directory;
3049     std::vector<String> files;
3050 };
3055 //########################################################################
3056 //# M A K E    B A S E
3057 //########################################################################
3058 /**
3059  * Base class for all classes in this file
3060  */
3061 class MakeBase
3063 public:
3065     MakeBase()
3066         { line = 0; }
3067     virtual ~MakeBase()
3068         {}
3070     /**
3071      *     Return the URI of the file associated with this object 
3072      */     
3073     URI getURI()
3074         { return uri; }
3076     /**
3077      * Set the uri to the given string
3078      */
3079     void setURI(const String &uristr)
3080         { uri.parse(uristr); }
3082     /**
3083      *  Resolve another path relative to this one
3084      */
3085     String resolve(const String &otherPath);
3087     /**
3088      * replace variable refs like ${a} with their values
3089      * Assume that the string has already been syntax validated
3090      */
3091     String eval(const String &s, const String &defaultVal);
3093     /**
3094      * replace variable refs like ${a} with their values
3095      * return true or false
3096      * Assume that the string has already been syntax validated
3097      */
3098     bool evalBool(const String &s, bool defaultVal);
3100     /**
3101      *  Get an element attribute, performing substitutions if necessary
3102      */
3103     bool getAttribute(Element *elem, const String &name, String &result);
3105     /**
3106      * Get an element value, performing substitutions if necessary
3107      */
3108     bool getValue(Element *elem, String &result);
3109     
3110     /**
3111      * Set the current line number in the file
3112      */         
3113     void setLine(int val)
3114         { line = val; }
3115         
3116     /**
3117      * Get the current line number in the file
3118      */         
3119     int getLine()
3120         { return line; }
3123     /**
3124      * Set a property to a given value
3125      */
3126     virtual void setProperty(const String &name, const String &val)
3127         {
3128         properties[name] = val;
3129         }
3131     /**
3132      * Return a named property is found, else a null string
3133      */
3134     virtual String getProperty(const String &name)
3135         {
3136         String val;
3137         std::map<String, String>::iterator iter = properties.find(name);
3138         if (iter != properties.end())
3139             val = iter->second;
3140         String sval;
3141         if (!getSubstitutions(val, sval))
3142             return false;
3143         return sval;
3144         }
3146     /**
3147      * Return true if a named property is found, else false
3148      */
3149     virtual bool hasProperty(const String &name)
3150         {
3151         std::map<String, String>::iterator iter = properties.find(name);
3152         if (iter == properties.end())
3153             return false;
3154         return true;
3155         }
3158 protected:
3160     /**
3161      *    The path to the file associated with this object
3162      */     
3163     URI uri;
3164     
3165     /**
3166      *    If this prefix is seen in a substitution, use an environment
3167      *    variable.
3168      *             example:  <property environment="env"/>
3169      *             ${env.JAVA_HOME}
3170      */
3171     String envPrefix;
3173     /**
3174      *    If this prefix is seen in a substitution, use as a
3175      *    pkg-config 'all' query
3176      *             example:  <property pkg-config="pc"/>
3177      *             ${pc.gtkmm}
3178      */
3179     String pcPrefix;
3181     /**
3182      *    If this prefix is seen in a substitution, use as a
3183      *    pkg-config 'cflags' query
3184      *             example:  <property pkg-config="pcc"/>
3185      *             ${pcc.gtkmm}
3186      */
3187     String pccPrefix;
3189     /**
3190      *    If this prefix is seen in a substitution, use as a
3191      *    pkg-config 'libs' query
3192      *             example:  <property pkg-config-libs="pcl"/>
3193      *             ${pcl.gtkmm}
3194      */
3195     String pclPrefix;
3197     /**
3198      *    If this prefix is seen in a substitution, use as a
3199      *    Bazaar "bzr revno" query
3200      *             example:  <property subversion="svn"/> ???
3201      *             ${bzr.Revision}
3202      */
3203     String bzrPrefix;
3209     /**
3210      *  Print a printf()-like formatted error message
3211      */
3212     void error(const char *fmt, ...);
3214     /**
3215      *  Print a printf()-like formatted trace message
3216      */
3217     void status(const char *fmt, ...);
3219     /**
3220      *  Show target status
3221      */
3222     void targetstatus(const char *fmt, ...);
3224     /**
3225      *  Print a printf()-like formatted trace message
3226      */
3227     void trace(const char *fmt, ...);
3229     /**
3230      *  Check if a given string matches a given regex pattern
3231      */
3232     bool regexMatch(const String &str, const String &pattern);
3234     /**
3235      *
3236      */
3237     String getSuffix(const String &fname);
3239     /**
3240      * Break up a string into substrings delimited the characters
3241      * in delimiters.  Null-length substrings are ignored
3242      */  
3243     std::vector<String> tokenize(const String &val,
3244                           const String &delimiters);
3246     /**
3247      *  replace runs of whitespace with a space
3248      */
3249     String strip(const String &s);
3251     /**
3252      *  remove leading whitespace from each line
3253      */
3254     String leftJustify(const String &s);
3256     /**
3257      *  remove leading and trailing whitespace from string
3258      */
3259     String trim(const String &s);
3261     /**
3262      *  Return a lower case version of the given string
3263      */
3264     String toLower(const String &s);
3266     /**
3267      * Return the native format of the canonical
3268      * path which we store
3269      */
3270     String getNativePath(const String &path);
3272     /**
3273      * Execute a shell command.  Outbuf is a ref to a string
3274      * to catch the result.     
3275      */         
3276     bool executeCommand(const String &call,
3277                         const String &inbuf,
3278                         String &outbuf,
3279                         String &errbuf);
3280     /**
3281      * List all directories in a given base and starting directory
3282      * It is usually called like:
3283      *        bool ret = listDirectories("src", "", result);    
3284      */         
3285     bool listDirectories(const String &baseName,
3286                          const String &dirname,
3287                          std::vector<String> &res);
3289     /**
3290      * Find all files in the named directory 
3291      */         
3292     bool listFiles(const String &baseName,
3293                    const String &dirname,
3294                    std::vector<String> &result);
3296     /**
3297      * Perform a listing for a fileset 
3298      */         
3299     bool listFiles(MakeBase &propRef, FileSet &fileSet);
3301     /**
3302      * Parse a <patternset>
3303      */  
3304     bool parsePatternSet(Element *elem,
3305                        MakeBase &propRef,
3306                        std::vector<String> &includes,
3307                        std::vector<String> &excludes);
3309     /**
3310      * Parse a <fileset> entry, and determine which files
3311      * should be included
3312      */  
3313     bool parseFileSet(Element *elem,
3314                     MakeBase &propRef,
3315                     FileSet &fileSet);
3316     /**
3317      * Parse a <filelist> entry
3318      */  
3319     bool parseFileList(Element *elem,
3320                     MakeBase &propRef,
3321                     FileList &fileList);
3323     /**
3324      * Return this object's property list
3325      */
3326     virtual std::map<String, String> &getProperties()
3327         { return properties; }
3330     std::map<String, String> properties;
3332     /**
3333      * Create a directory, making intermediate dirs
3334      * if necessary
3335      */                  
3336     bool createDirectory(const String &dirname);
3338     /**
3339      * Delete a directory and its children if desired
3340      */
3341     bool removeDirectory(const String &dirName);
3343     /**
3344      * Copy a file from one name to another. Perform only if needed
3345      */ 
3346     bool copyFile(const String &srcFile, const String &destFile);
3348     /**
3349      * Delete a file
3350      */ 
3351     bool removeFile(const String &file);
3353     /**
3354      * Tests if the file exists
3355      */ 
3356     bool fileExists(const String &fileName);
3358     /**
3359      * Tests if the file exists and is a regular file
3360      */ 
3361     bool isRegularFile(const String &fileName);
3363     /**
3364      * Tests if the file exists and is a directory
3365      */ 
3366     bool isDirectory(const String &fileName);
3368     /**
3369      * Tests is the modification date of fileA is newer than fileB
3370      */ 
3371     bool isNewerThan(const String &fileA, const String &fileB);
3373 private:
3375     bool pkgConfigRecursive(const String packageName,
3376                             const String &path, 
3377                             const String &prefix, 
3378                             int query,
3379                             String &result,
3380                             std::set<String> &deplist);
3382     /**
3383      * utility method to query for "all", "cflags", or "libs" for this package and its
3384      * dependencies.  0, 1, 2
3385      */          
3386     bool pkgConfigQuery(const String &packageName, int query, String &result);
3388     /**
3389      * replace a variable ref like ${a} with a value
3390      */
3391     bool lookupProperty(const String &s, String &result);
3392     
3393     /**
3394      * called by getSubstitutions().  This is in case a looked-up string
3395      * has substitutions also.     
3396      */
3397     bool getSubstitutionsRecursive(const String &s, String &result, int depth);
3399     /**
3400      * replace variable refs in a string like ${a} with their values
3401      */
3402     bool getSubstitutions(const String &s, String &result);
3404     int line;
3407 };
3411 /**
3412  * Define the pkg-config class here, since it will be used in MakeBase method
3413  * implementations. 
3414  */
3415 class PkgConfig : public MakeBase
3418 public:
3420     /**
3421      *
3422      */
3423     PkgConfig()
3424         {
3425          path   = ".";
3426          prefix = "/target";
3427          init();
3428          }
3430     /**
3431      *
3432      */
3433     PkgConfig(const PkgConfig &other)
3434         { assign(other); }
3436     /**
3437      *
3438      */
3439     PkgConfig &operator=(const PkgConfig &other)
3440         { assign(other); return *this; }
3442     /**
3443      *
3444      */
3445     virtual ~PkgConfig()
3446         { }
3448     /**
3449      *
3450      */
3451     virtual String getName()
3452         { return name; }
3454     /**
3455      *
3456      */
3457     virtual String getPath()
3458         { return path; }
3460     /**
3461      *
3462      */
3463     virtual void setPath(const String &val)
3464         { path = val; }
3466     /**
3467      *
3468      */
3469     virtual String getPrefix()
3470         { return prefix; }
3472     /**
3473      *  Allow the user to override the prefix in the file
3474      */
3475     virtual void setPrefix(const String &val)
3476         { prefix = val; }
3478     /**
3479      *
3480      */
3481     virtual String getDescription()
3482         { return description; }
3484     /**
3485      *
3486      */
3487     virtual String getCflags()
3488         { return cflags; }
3490     /**
3491      *
3492      */
3493     virtual String getLibs()
3494         { return libs; }
3496     /**
3497      *
3498      */
3499     virtual String getAll()
3500         {
3501          String ret = cflags;
3502          ret.append(" ");
3503          ret.append(libs);
3504          return ret;
3505         }
3507     /**
3508      *
3509      */
3510     virtual String getVersion()
3511         { return version; }
3513     /**
3514      *
3515      */
3516     virtual int getMajorVersion()
3517         { return majorVersion; }
3519     /**
3520      *
3521      */
3522     virtual int getMinorVersion()
3523         { return minorVersion; }
3525     /**
3526      *
3527      */
3528     virtual int getMicroVersion()
3529         { return microVersion; }
3531     /**
3532      *
3533      */
3534     virtual std::map<String, String> &getAttributes()
3535         { return attrs; }
3537     /**
3538      *
3539      */
3540     virtual std::vector<String> &getRequireList()
3541         { return requireList; }
3543     /**
3544      *  Read a file for its details
3545      */         
3546     virtual bool readFile(const String &fileName);
3548     /**
3549      *  Read a file for its details
3550      */         
3551     virtual bool query(const String &name);
3553 private:
3555     void init()
3556         {
3557         //do not set path and prefix here
3558         name         = "";
3559         description  = "";
3560         cflags       = "";
3561         libs         = "";
3562         requires     = "";
3563         version      = "";
3564         majorVersion = 0;
3565         minorVersion = 0;
3566         microVersion = 0;
3567         fileName     = "";
3568         attrs.clear();
3569         requireList.clear();
3570         }
3572     void assign(const PkgConfig &other)
3573         {
3574         name         = other.name;
3575         path         = other.path;
3576         prefix       = other.prefix;
3577         description  = other.description;
3578         cflags       = other.cflags;
3579         libs         = other.libs;
3580         requires     = other.requires;
3581         version      = other.version;
3582         majorVersion = other.majorVersion;
3583         minorVersion = other.minorVersion;
3584         microVersion = other.microVersion;
3585         fileName     = other.fileName;
3586         attrs        = other.attrs;
3587         requireList  = other.requireList;
3588         }
3592     int get(int pos);
3594     int skipwhite(int pos);
3596     int getword(int pos, String &ret);
3598     /**
3599      * Very important
3600      */         
3601     bool parseRequires();
3603     void parseVersion();
3605     bool parseLine(const String &lineBuf);
3607     bool parse(const String &buf);
3609     void dumpAttrs();
3611     String name;
3613     String path;
3615     String prefix;
3617     String description;
3619     String cflags;
3621     String libs;
3623     String requires;
3625     String version;
3627     int majorVersion;
3629     int minorVersion;
3631     int microVersion;
3633     String fileName;
3635     std::map<String, String> attrs;
3637     std::vector<String> requireList;
3639     char *parsebuf;
3640     int parselen;
3641 };
3643 /**
3644  * Execute the "bzr revno" command and return the result.
3645  * This is a simple, small class.
3646  */
3647 class BzrRevno : public MakeBase
3649 public:
3651     /**
3652      * Safe way. Execute "bzr revno" and return the result.
3653      * Safe from changes in format.
3654      */
3655     bool query(String &res)
3656     {
3657         String cmd = "bzr revno";
3659         String outString, errString;
3660         bool ret = executeCommand(cmd.c_str(), "", outString, errString);
3661         if (!ret)
3662             {
3663             error("error executing '%s': %s", cmd.c_str(), errString.c_str());
3664             return false;
3665             }
3666         res = outString;
3667         return true;
3668     } 
3669 };
3671 /**
3672  * Execute the "svn info" command and parse the result.
3673  * This is a simple, small class. Define here, because it
3674  * is used by MakeBase implementation methods. 
3675  */
3676 class SvnInfo : public MakeBase
3678 public:
3680 #if 0
3681     /**
3682      * Safe way. Execute "svn info --xml" and parse the result.  Search for
3683      * elements/attributes.  Safe from changes in format.
3684      */
3685     bool query(const String &name, String &res)
3686     {
3687         String cmd = "svn info --xml";
3688     
3689         String outString, errString;
3690         bool ret = executeCommand(cmd.c_str(), "", outString, errString);
3691         if (!ret)
3692             {
3693             error("error executing '%s': %s", cmd.c_str(), errString.c_str());
3694             return false;
3695             }
3696         Parser parser;
3697         Element *elem = parser.parse(outString); 
3698         if (!elem)
3699             {
3700             error("error parsing 'svn info' xml result: %s", outString.c_str());
3701             return false;
3702             }
3703         
3704         res = elem->getTagValue(name);
3705         if (res.size()==0)
3706             {
3707             res = elem->getTagAttribute("entry", name);
3708             }
3709         return true;
3710     } 
3711 #else
3714     /**
3715      * Universal way.  Parse the file directly.  Not so safe from
3716      * changes in format.
3717      */
3718     bool query(const String &name, String &res)
3719     {
3720         String fileName = resolve(".svn/entries");
3721         String nFileName = getNativePath(fileName);
3722         
3723         std::map<String, String> properties;
3724         
3725         FILE *f = fopen(nFileName.c_str(), "r");
3726         if (!f)
3727             {
3728             error("could not open SVN 'entries' file");
3729             return false;
3730             }
3732         const char *fieldNames[] =
3733             {
3734             "format-nbr",
3735             "name",
3736             "kind",
3737             "revision",
3738             "url",
3739             "repos",
3740             "schedule",
3741             "text-time",
3742             "checksum",
3743             "committed-date",
3744             "committed-rev",
3745             "last-author",
3746             "has-props",
3747             "has-prop-mods",
3748             "cachable-props",
3749             };
3751         for (int i=0 ; i<15 ; i++)
3752             {
3753             inbuf[0] = '\0';
3754             if (feof(f) || !fgets(inbuf, 255, f))
3755                 break;
3756             properties[fieldNames[i]] = trim(inbuf);
3757             }
3758         fclose(f);
3759         
3760         res = properties[name];
3761         
3762         return true;
3763     } 
3764     
3765 private:
3767     char inbuf[256];
3769 #endif
3771 };
3778 /**
3779  *  Print a printf()-like formatted error message
3780  */
3781 void MakeBase::error(const char *fmt, ...)
3783     va_list args;
3784     va_start(args,fmt);
3785     fprintf(stderr, "Make error line %d: ", line);
3786     vfprintf(stderr, fmt, args);
3787     fprintf(stderr, "\n");
3788     va_end(args) ;
3793 /**
3794  *  Print a printf()-like formatted trace message
3795  */
3796 void MakeBase::status(const char *fmt, ...)
3798     va_list args;
3799     //fprintf(stdout, " ");
3800     va_start(args,fmt);
3801     vfprintf(stdout, fmt, args);
3802     va_end(args);
3803     fprintf(stdout, "\n");
3804     fflush(stdout);
3808 /**
3809  *  Print a printf()-like formatted trace message
3810  */
3811 void MakeBase::trace(const char *fmt, ...)
3813     va_list args;
3814     fprintf(stdout, "Make: ");
3815     va_start(args,fmt);
3816     vfprintf(stdout, fmt, args);
3817     va_end(args) ;
3818     fprintf(stdout, "\n");
3819     fflush(stdout);
3824 /**
3825  *  Resolve another path relative to this one
3826  */
3827 String MakeBase::resolve(const String &otherPath)
3829     URI otherURI(otherPath);
3830     URI fullURI = uri.resolve(otherURI);
3831     String ret = fullURI.toString();
3832     return ret;
3837 /**
3838  *  Check if a given string matches a given regex pattern
3839  */
3840 bool MakeBase::regexMatch(const String &str, const String &pattern)
3842     const TRexChar *terror = NULL;
3843     const TRexChar *cpat = pattern.c_str();
3844     TRex *expr = trex_compile(cpat, &terror);
3845     if (!expr)
3846         {
3847         if (!terror)
3848             terror = "undefined";
3849         error("compilation error [%s]!\n", terror);
3850         return false;
3851         } 
3853     bool ret = true;
3855     const TRexChar *cstr = str.c_str();
3856     if (trex_match(expr, cstr))
3857         {
3858         ret = true;
3859         }
3860     else
3861         {
3862         ret = false;
3863         }
3865     trex_free(expr);
3867     return ret;
3870 /**
3871  *  Return the suffix, if any, of a file name
3872  */
3873 String MakeBase::getSuffix(const String &fname)
3875     if (fname.size() < 2)
3876         return "";
3877     unsigned int pos = fname.find_last_of('.');
3878     if (pos == fname.npos)
3879         return "";
3880     pos++;
3881     String res = fname.substr(pos, fname.size()-pos);
3882     //trace("suffix:%s", res.c_str()); 
3883     return res;
3888 /**
3889  * Break up a string into substrings delimited the characters
3890  * in delimiters.  Null-length substrings are ignored
3891  */  
3892 std::vector<String> MakeBase::tokenize(const String &str,
3893                                 const String &delimiters)
3896     std::vector<String> res;
3897     char *del = (char *)delimiters.c_str();
3898     String dmp;
3899     for (unsigned int i=0 ; i<str.size() ; i++)
3900         {
3901         char ch = str[i];
3902         char *p = (char *)0;
3903         for (p=del ; *p ; p++)
3904             if (*p == ch)
3905                 break;
3906         if (*p)
3907             {
3908             if (dmp.size() > 0)
3909                 {
3910                 res.push_back(dmp);
3911                 dmp.clear();
3912                 }
3913             }
3914         else
3915             {
3916             dmp.push_back(ch);
3917             }
3918         }
3919     //Add tail
3920     if (dmp.size() > 0)
3921         {
3922         res.push_back(dmp);
3923         dmp.clear();
3924         }
3926     return res;
3931 /**
3932  *  replace runs of whitespace with a single space
3933  */
3934 String MakeBase::strip(const String &s)
3936     int len = s.size();
3937     String stripped;
3938     for (int i = 0 ; i<len ; i++)
3939         {
3940         char ch = s[i];
3941         if (isspace(ch))
3942             {
3943             stripped.push_back(' ');
3944             for ( ; i<len ; i++)
3945                 {
3946                 ch = s[i];
3947                 if (!isspace(ch))
3948                     {
3949                     stripped.push_back(ch);
3950                     break;
3951                     }
3952                 }
3953             }
3954         else
3955             {
3956             stripped.push_back(ch);
3957             }
3958         }
3959     return stripped;
3962 /**
3963  *  remove leading whitespace from each line
3964  */
3965 String MakeBase::leftJustify(const String &s)
3967     String out;
3968     int len = s.size();
3969     for (int i = 0 ; i<len ; )
3970         {
3971         char ch;
3972         //Skip to first visible character
3973         while (i<len)
3974             {
3975             ch = s[i];
3976             if (ch == '\n' || ch == '\r'
3977               || !isspace(ch))
3978                   break;
3979             i++;
3980             }
3981         //Copy the rest of the line
3982         while (i<len)
3983             {
3984             ch = s[i];
3985             if (ch == '\n' || ch == '\r')
3986                 {
3987                 if (ch != '\r')
3988                     out.push_back('\n');
3989                 i++;
3990                 break;
3991                 }
3992             else
3993                 {
3994                 out.push_back(ch);
3995                 }
3996             i++;
3997             }
3998         }
3999     return out;
4003 /**
4004  *  Removes whitespace from beginning and end of a string
4005  */
4006 String MakeBase::trim(const String &s)
4008     if (s.size() < 1)
4009         return s;
4010     
4011     //Find first non-ws char
4012     unsigned int begin = 0;
4013     for ( ; begin < s.size() ; begin++)
4014         {
4015         if (!isspace(s[begin]))
4016             break;
4017         }
4019     //Find first non-ws char, going in reverse
4020     unsigned int end = s.size() - 1;
4021     for ( ; end > begin ; end--)
4022         {
4023         if (!isspace(s[end]))
4024             break;
4025         }
4026     //trace("begin:%d  end:%d", begin, end);
4028     String res = s.substr(begin, end-begin+1);
4029     return res;
4033 /**
4034  *  Return a lower case version of the given string
4035  */
4036 String MakeBase::toLower(const String &s)
4038     if (s.size()==0)
4039         return s;
4041     String ret;
4042     for(unsigned int i=0; i<s.size() ; i++)
4043         {
4044         ret.push_back(tolower(s[i]));
4045         }
4046     return ret;
4050 /**
4051  * Return the native format of the canonical
4052  * path which we store
4053  */
4054 String MakeBase::getNativePath(const String &path)
4056 #ifdef __WIN32__
4057     String npath;
4058     unsigned int firstChar = 0;
4059     if (path.size() >= 3)
4060         {
4061         if (path[0] == '/' &&
4062             isalpha(path[1]) &&
4063             path[2] == ':')
4064             firstChar++;
4065         }
4066     for (unsigned int i=firstChar ; i<path.size() ; i++)
4067         {
4068         char ch = path[i];
4069         if (ch == '/')
4070             npath.push_back('\\');
4071         else
4072             npath.push_back(ch);
4073         }
4074     return npath;
4075 #else
4076     return path;
4077 #endif
4081 #ifdef __WIN32__
4082 #include <tchar.h>
4084 static String win32LastError()
4087     DWORD dw = GetLastError(); 
4089     LPVOID str;
4090     FormatMessage(
4091         FORMAT_MESSAGE_ALLOCATE_BUFFER | 
4092         FORMAT_MESSAGE_FROM_SYSTEM,
4093         NULL,
4094         dw,
4095         0,
4096         (LPTSTR) &str,
4097         0, NULL );
4098     LPTSTR p = _tcschr((const char *)str, _T('\r'));
4099     if(p != NULL)
4100         { // lose CRLF
4101         *p = _T('\0');
4102         }
4103     String ret = (char *)str;
4104     LocalFree(str);
4106     return ret;
4108 #endif
4113 #ifdef __WIN32__
4115 /**
4116  * Execute a system call, using pipes to send data to the
4117  * program's stdin,  and reading stdout and stderr.
4118  */
4119 bool MakeBase::executeCommand(const String &command,
4120                               const String &inbuf,
4121                               String &outbuf,
4122                               String &errbuf)
4125     status("============ cmd ============\n%s\n=============================",
4126                 command.c_str());
4128     outbuf.clear();
4129     errbuf.clear();
4130     
4132     /*
4133     I really hate having win32 code in this program, but the
4134     read buffer in command.com and cmd.exe are just too small
4135     for the large commands we need for compiling and linking.
4136     */
4138     bool ret = true;
4140     //# Allocate a separate buffer for safety
4141     char *paramBuf = new char[command.size() + 1];
4142     if (!paramBuf)
4143        {
4144        error("executeCommand cannot allocate command buffer");
4145        return false;
4146        }
4147     strcpy(paramBuf, (char *)command.c_str());
4149     //# Go to http://msdn2.microsoft.com/en-us/library/ms682499.aspx
4150     //# to see how Win32 pipes work
4152     //# Create pipes
4153     SECURITY_ATTRIBUTES saAttr; 
4154     saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); 
4155     saAttr.bInheritHandle = TRUE; 
4156     saAttr.lpSecurityDescriptor = NULL; 
4157     HANDLE stdinRead,  stdinWrite;
4158     HANDLE stdoutRead, stdoutWrite;
4159     HANDLE stderrRead, stderrWrite;
4160     if (!CreatePipe(&stdinRead, &stdinWrite, &saAttr, 0))
4161         {
4162         error("executeProgram: could not create pipe");
4163         delete[] paramBuf;
4164         return false;
4165         } 
4166     SetHandleInformation(stdinWrite, HANDLE_FLAG_INHERIT, 0);
4167     if (!CreatePipe(&stdoutRead, &stdoutWrite, &saAttr, 0))
4168         {
4169         error("executeProgram: could not create pipe");
4170         delete[] paramBuf;
4171         return false;
4172         } 
4173     SetHandleInformation(stdoutRead, HANDLE_FLAG_INHERIT, 0);
4174     if (&outbuf != &errbuf) {
4175         if (!CreatePipe(&stderrRead, &stderrWrite, &saAttr, 0))
4176             {
4177             error("executeProgram: could not create pipe");
4178             delete[] paramBuf;
4179             return false;
4180             } 
4181         SetHandleInformation(stderrRead, HANDLE_FLAG_INHERIT, 0);
4182     } else {
4183         stderrRead = stdoutRead;
4184         stderrWrite = stdoutWrite;
4185     }
4187     // Create the process
4188     STARTUPINFO siStartupInfo;
4189     PROCESS_INFORMATION piProcessInfo;
4190     memset(&siStartupInfo, 0, sizeof(siStartupInfo));
4191     memset(&piProcessInfo, 0, sizeof(piProcessInfo));
4192     siStartupInfo.cb = sizeof(siStartupInfo);
4193     siStartupInfo.hStdError   =  stderrWrite;
4194     siStartupInfo.hStdOutput  =  stdoutWrite;
4195     siStartupInfo.hStdInput   =  stdinRead;
4196     siStartupInfo.dwFlags    |=  STARTF_USESTDHANDLES;
4197    
4198     if (!CreateProcess(NULL, paramBuf, NULL, NULL, true,
4199                 0, NULL, NULL, &siStartupInfo,
4200                 &piProcessInfo))
4201         {
4202         error("executeCommand : could not create process : %s",
4203                     win32LastError().c_str());
4204         ret = false;
4205         }
4207     delete[] paramBuf;
4209     DWORD bytesWritten;
4210     if (inbuf.size()>0 &&
4211         !WriteFile(stdinWrite, inbuf.c_str(), inbuf.size(), 
4212                &bytesWritten, NULL))
4213         {
4214         error("executeCommand: could not write to pipe");
4215         return false;
4216         }    
4217     if (!CloseHandle(stdinWrite))
4218         {          
4219         error("executeCommand: could not close write pipe");
4220         return false;
4221         }
4222     if (!CloseHandle(stdoutWrite))
4223         {
4224         error("executeCommand: could not close read pipe");
4225         return false;
4226         }
4227     if (stdoutWrite != stderrWrite && !CloseHandle(stderrWrite))
4228         {
4229         error("executeCommand: could not close read pipe");
4230         return false;
4231         }
4233     bool lastLoop = false;
4234     while (true)
4235         {
4236         DWORD avail;
4237         DWORD bytesRead;
4238         char readBuf[4096];
4240         //trace("## stderr");
4241         PeekNamedPipe(stderrRead, NULL, 0, NULL, &avail, NULL);
4242         if (avail > 0)
4243             {
4244             bytesRead = 0;
4245             if (avail>4096) avail = 4096;
4246             ReadFile(stderrRead, readBuf, avail, &bytesRead, NULL);
4247             if (bytesRead > 0)
4248                 {
4249                 for (unsigned int i=0 ; i<bytesRead ; i++)
4250                     errbuf.push_back(readBuf[i]);
4251                 }
4252             }
4254         //trace("## stdout");
4255         PeekNamedPipe(stdoutRead, NULL, 0, NULL, &avail, NULL);
4256         if (avail > 0)
4257             {
4258             bytesRead = 0;
4259             if (avail>4096) avail = 4096;
4260             ReadFile(stdoutRead, readBuf, avail, &bytesRead, NULL);
4261             if (bytesRead > 0)
4262                 {
4263                 for (unsigned int i=0 ; i<bytesRead ; i++)
4264                     outbuf.push_back(readBuf[i]);
4265                 }
4266             }
4267             
4268         //Was this the final check after program done?
4269         if (lastLoop)
4270             break;
4272         DWORD exitCode;
4273         GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
4274         if (exitCode != STILL_ACTIVE)
4275             lastLoop = true;
4277         Sleep(10);
4278         }    
4279     //trace("outbuf:%s", outbuf.c_str());
4280     if (!CloseHandle(stdoutRead))
4281         {
4282         error("executeCommand: could not close read pipe");
4283         return false;
4284         }
4285     if (stdoutRead != stderrRead && !CloseHandle(stderrRead))
4286         {
4287         error("executeCommand: could not close read pipe");
4288         return false;
4289         }
4291     DWORD exitCode;
4292     GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
4293     //trace("exit code:%d", exitCode);
4294     if (exitCode != 0)
4295         {
4296         ret = false;
4297         }
4298     
4299     CloseHandle(piProcessInfo.hProcess);
4300     CloseHandle(piProcessInfo.hThread);
4302     return ret;
4304
4306 #else  /*do it unix style*/
4308 #include <sys/wait.h>
4312 /**
4313  * Execute a system call, using pipes to send data to the
4314  * program's stdin,  and reading stdout and stderr.
4315  */
4316 bool MakeBase::executeCommand(const String &command,
4317                               const String &inbuf,
4318                               String &outbuf,
4319                               String &errbuf)
4322     status("============ cmd ============\n%s\n=============================",
4323                 command.c_str());
4325     outbuf.clear();
4326     errbuf.clear();
4327     
4329     int outfds[2];
4330     if (pipe(outfds) < 0)
4331         return false;
4332     int errfds[2];
4333     if (pipe(errfds) < 0)
4334         return false;
4335     int pid = fork();
4336     if (pid < 0)
4337         {
4338         close(outfds[0]);
4339         close(outfds[1]);
4340         close(errfds[0]);
4341         close(errfds[1]);
4342         error("launch of command '%s' failed : %s",
4343              command.c_str(), strerror(errno));
4344         return false;
4345         }
4346     else if (pid > 0) // parent
4347         {
4348         close(outfds[1]);
4349         close(errfds[1]);
4350         }
4351     else // == 0, child
4352         {
4353         close(outfds[0]);
4354         dup2(outfds[1], STDOUT_FILENO);
4355         close(outfds[1]);
4356         close(errfds[0]);
4357         dup2(errfds[1], STDERR_FILENO);
4358         close(errfds[1]);
4360         char *args[4];
4361         args[0] = (char *)"sh";
4362         args[1] = (char *)"-c";
4363         args[2] = (char *)command.c_str();
4364         args[3] = NULL;
4365         execv("/bin/sh", args);
4366         exit(EXIT_FAILURE);
4367         }
4369     String outb;
4370     String errb;
4372     int outRead = outfds[0];
4373     int errRead = errfds[0];
4374     int max = outRead;
4375     if (errRead > max)
4376         max = errRead;
4378     bool outOpen = true;
4379     bool errOpen = true;
4381     while (outOpen || errOpen)
4382         {
4383         char ch;
4384         fd_set fdset;
4385         FD_ZERO(&fdset);
4386         if (outOpen)
4387             FD_SET(outRead, &fdset);
4388         if (errOpen)
4389             FD_SET(errRead, &fdset);
4390         int ret = select(max+1, &fdset, NULL, NULL, NULL);
4391         if (ret < 0)
4392             break;
4393         if (FD_ISSET(outRead, &fdset))
4394             {
4395             if (read(outRead, &ch, 1) <= 0)
4396                 { outOpen = false; }
4397             else if (ch <= 0)
4398                 { /* outOpen = false; */ }
4399             else
4400                 { outb.push_back(ch); }
4401             }
4402         if (FD_ISSET(errRead, &fdset))
4403             {
4404             if (read(errRead, &ch, 1) <= 0)
4405                 { errOpen = false; }
4406             else if (ch <= 0)
4407                 { /* errOpen = false; */ }
4408             else
4409                 { errb.push_back(ch); }
4410             }
4411         }
4413     int childReturnValue;
4414     wait(&childReturnValue);
4416     close(outRead);
4417     close(errRead);
4419     outbuf = outb;
4420     errbuf = errb;
4422     if (childReturnValue != 0)
4423         {
4424         error("exec of command '%s' failed : %s",
4425              command.c_str(), strerror(childReturnValue));
4426         return false;
4427         }
4429     return true;
4430
4432 #endif
4437 bool MakeBase::listDirectories(const String &baseName,
4438                               const String &dirName,
4439                               std::vector<String> &res)
4441     res.push_back(dirName);
4442     String fullPath = baseName;
4443     if (dirName.size()>0)
4444         {
4445         if (dirName[0]!='/') fullPath.append("/");
4446         fullPath.append(dirName);
4447         }
4448     DIR *dir = opendir(fullPath.c_str());
4449     while (true)
4450         {
4451         struct dirent *de = readdir(dir);
4452         if (!de)
4453             break;
4455         //Get the directory member name
4456         String s = de->d_name;
4457         if (s.size() == 0 || s[0] == '.')
4458             continue;
4459         String childName = dirName;
4460         childName.append("/");
4461         childName.append(s);
4463         String fullChildPath = baseName;
4464         fullChildPath.append("/");
4465         fullChildPath.append(childName);
4466         struct stat finfo;
4467         String childNative = getNativePath(fullChildPath);
4468         if (cachedStat(childNative, &finfo)<0)
4469             {
4470             error("cannot stat file:%s", childNative.c_str());
4471             }
4472         else if (S_ISDIR(finfo.st_mode))
4473             {
4474             //trace("directory: %s", childName.c_str());
4475             if (!listDirectories(baseName, childName, res))
4476                 return false;
4477             }
4478         }
4479     closedir(dir);
4481     return true;
4485 bool MakeBase::listFiles(const String &baseDir,
4486                          const String &dirName,
4487                          std::vector<String> &res)
4489     String fullDir = baseDir;
4490     if (dirName.size()>0)
4491         {
4492         fullDir.append("/");
4493         fullDir.append(dirName);
4494         }
4495     String dirNative = getNativePath(fullDir);
4497     std::vector<String> subdirs;
4498     DIR *dir = opendir(dirNative.c_str());
4499     if (!dir)
4500         {
4501         error("Could not open directory %s : %s",
4502               dirNative.c_str(), strerror(errno));
4503         return false;
4504         }
4505     while (true)
4506         {
4507         struct dirent *de = readdir(dir);
4508         if (!de)
4509             break;
4511         //Get the directory member name
4512         String s = de->d_name;
4513         if (s.size() == 0 || s[0] == '.')
4514             continue;
4515         String childName;
4516         if (dirName.size()>0)
4517             {
4518             childName.append(dirName);
4519             childName.append("/");
4520             }
4521         childName.append(s);
4522         String fullChild = baseDir;
4523         fullChild.append("/");
4524         fullChild.append(childName);
4525         
4526         if (isDirectory(fullChild))
4527             {
4528             //trace("directory: %s", childName.c_str());
4529             if (!listFiles(baseDir, childName, res))
4530                 return false;
4531             continue;
4532             }
4533         else if (!isRegularFile(fullChild))
4534             {
4535             error("unknown file:%s", childName.c_str());
4536             return false;
4537             }
4539        //all done!
4540         res.push_back(childName);
4542         }
4543     closedir(dir);
4545     return true;
4549 /**
4550  * Several different classes extend MakeBase.  By "propRef", we mean
4551  * the one holding the properties.  Likely "Make" itself
4552  */
4553 bool MakeBase::listFiles(MakeBase &propRef, FileSet &fileSet)
4555     //before doing the list,  resolve any property references
4556     //that might have been specified in the directory name, such as ${src}
4557     String fsDir = fileSet.getDirectory();
4558     String dir;
4559     if (!propRef.getSubstitutions(fsDir, dir))
4560         return false;
4561     String baseDir = propRef.resolve(dir);
4562     std::vector<String> fileList;
4563     if (!listFiles(baseDir, "", fileList))
4564         return false;
4566     std::vector<String> includes = fileSet.getIncludes();
4567     std::vector<String> excludes = fileSet.getExcludes();
4569     std::vector<String> incs;
4570     std::vector<String>::iterator iter;
4572     std::sort(fileList.begin(), fileList.end());
4574     //If there are <includes>, then add files to the output
4575     //in the order of the include list
4576     if (includes.size()==0)
4577         incs = fileList;
4578     else
4579         {
4580         for (iter = includes.begin() ; iter != includes.end() ; iter++)
4581             {
4582             String &pattern = *iter;
4583             std::vector<String>::iterator siter;
4584             for (siter = fileList.begin() ; siter != fileList.end() ; siter++)
4585                 {
4586                 String s = *siter;
4587                 if (regexMatch(s, pattern))
4588                     {
4589                     //trace("INCLUDED:%s", s.c_str());
4590                     incs.push_back(s);
4591                     }
4592                 }
4593             }
4594         }
4596     //Now trim off the <excludes>
4597     std::vector<String> res;
4598     for (iter = incs.begin() ; iter != incs.end() ; iter++)
4599         {
4600         String s = *iter;
4601         bool skipme = false;
4602         std::vector<String>::iterator siter;
4603         for (siter = excludes.begin() ; siter != excludes.end() ; siter++)
4604             {
4605             String &pattern = *siter;
4606             if (regexMatch(s, pattern))
4607                 {
4608                 //trace("EXCLUDED:%s", s.c_str());
4609                 skipme = true;
4610                 break;
4611                 }
4612             }
4613         if (!skipme)
4614             res.push_back(s);
4615         }
4616         
4617     fileSet.setFiles(res);
4619     return true;
4623 /**
4624  * 0 == all, 1 = cflags, 2 = libs
4625  */ 
4626 bool MakeBase::pkgConfigRecursive(const String packageName,
4627                                   const String &path, 
4628                                   const String &prefix, 
4629                                   int query,
4630                                   String &result,
4631                                   std::set<String> &deplist) 
4633     PkgConfig pkgConfig;
4634     if (path.size() > 0)
4635         pkgConfig.setPath(path);
4636     if (prefix.size() > 0)
4637         pkgConfig.setPrefix(prefix);
4638     if (!pkgConfig.query(packageName))
4639         return false;
4640     if (query == 0)
4641         result = pkgConfig.getAll();
4642     else if (query == 1)
4643         result = pkgConfig.getCflags();
4644     else
4645         result = pkgConfig.getLibs();
4646     deplist.insert(packageName);
4647     std::vector<String> list = pkgConfig.getRequireList();
4648     for (unsigned int i = 0 ; i<list.size() ; i++)
4649         {
4650         String depPkgName = list[i];
4651         if (deplist.find(depPkgName) != deplist.end())
4652             continue;
4653         String val;
4654         if (!pkgConfigRecursive(depPkgName, path, prefix, query, val, deplist))
4655             {
4656             error("Based on 'requires' attribute of package '%s'", packageName.c_str());
4657             return false;
4658             }
4659         result.append(" ");
4660         result.append(val);
4661         }
4663     return true;
4666 bool MakeBase::pkgConfigQuery(const String &packageName, int query, String &result)
4668     std::set<String> deplist;
4669     String path = getProperty("pkg-config-path");
4670     if (path.size()>0)
4671         path = resolve(path);
4672     String prefix = getProperty("pkg-config-prefix");
4673     String val;
4674     if (!pkgConfigRecursive(packageName, path, prefix, query, val, deplist))
4675         return false;
4676     result = val;
4677     return true;
4682 /**
4683  * replace a variable ref like ${a} with a value
4684  */
4685 bool MakeBase::lookupProperty(const String &propertyName, String &result)
4687     String varname = propertyName;
4688     if (envPrefix.size() > 0 &&
4689         varname.compare(0, envPrefix.size(), envPrefix) == 0)
4690         {
4691         varname = varname.substr(envPrefix.size());
4692         char *envstr = getenv(varname.c_str());
4693         if (!envstr)
4694             {
4695             error("environment variable '%s' not defined", varname.c_str());
4696             return false;
4697             }
4698         result = envstr;
4699         }
4700     else if (pcPrefix.size() > 0 &&
4701         varname.compare(0, pcPrefix.size(), pcPrefix) == 0)
4702         {
4703         varname = varname.substr(pcPrefix.size());
4704         String val;
4705         if (!pkgConfigQuery(varname, 0, val))
4706             return false;
4707         result = val;
4708         }
4709     else if (pccPrefix.size() > 0 &&
4710         varname.compare(0, pccPrefix.size(), pccPrefix) == 0)
4711         {
4712         varname = varname.substr(pccPrefix.size());
4713         String val;
4714         if (!pkgConfigQuery(varname, 1, val))
4715             return false;
4716         result = val;
4717         }
4718     else if (pclPrefix.size() > 0 &&
4719         varname.compare(0, pclPrefix.size(), pclPrefix) == 0)
4720         {
4721         varname = varname.substr(pclPrefix.size());
4722         String val;
4723         if (!pkgConfigQuery(varname, 2, val))
4724             return false;
4725         result = val;
4726         }
4727     else if (bzrPrefix.size() > 0 &&
4728         varname.compare(0, bzrPrefix.size(), bzrPrefix) == 0)
4729         {
4730         varname = varname.substr(bzrPrefix.size());
4731         String val;
4732         //SvnInfo svnInfo;
4733         BzrRevno bzrRevno;
4734         if (varname == "revision")
4735             {
4736             if (!bzrRevno.query(val))
4737                 return "";
4738             result = "r"+val;
4739         }
4740         /*if (!svnInfo.query(varname, val))
4741             return false;
4742         result = val;*/
4743         }
4744     else
4745         {
4746         std::map<String, String>::iterator iter;
4747         iter = properties.find(varname);
4748         if (iter != properties.end())
4749             {
4750             result = iter->second;
4751             }
4752         else
4753             {
4754             error("property '%s' not found", varname.c_str());
4755             return false;
4756             }
4757         }
4758     return true;
4764 /**
4765  * Analyse a string, looking for any substitutions or other
4766  * things that need resolution 
4767  */
4768 bool MakeBase::getSubstitutionsRecursive(const String &str,
4769                                          String &result, int depth)
4771     if (depth > 10)
4772         {
4773         error("nesting of substitutions too deep (>10) for '%s'",
4774                         str.c_str());
4775         return false;
4776         }
4777     String s = trim(str);
4778     int len = (int)s.size();
4779     String val;
4780     for (int i=0 ; i<len ; i++)
4781         {
4782         char ch = s[i];
4783         if (ch == '$' && s[i+1] == '{')
4784             {
4785             String varname;
4786             int j = i+2;
4787             for ( ; j<len ; j++)
4788                 {
4789                 ch = s[j];
4790                 if (ch == '$' && s[j+1] == '{')
4791                     {
4792                     error("attribute %s cannot have nested variable references",
4793                            s.c_str());
4794                     return false;
4795                     }
4796                 else if (ch == '}')
4797                     {
4798                     varname = trim(varname);
4799                     String varval;
4800                     if (!lookupProperty(varname, varval))
4801                         return false;
4802                     String varval2;
4803                     //Now see if the answer has ${} in it, too
4804                     if (!getSubstitutionsRecursive(varval, varval2, depth + 1))
4805                         return false;
4806                     val.append(varval2);
4807                     break;
4808                     }
4809                 else
4810                     {
4811                     varname.push_back(ch);
4812                     }
4813                 }
4814             i = j;
4815             }
4816         else
4817             {
4818             val.push_back(ch);
4819             }
4820         }
4821     result = val;
4822     return true;
4825 /**
4826  * Analyse a string, looking for any substitutions or other
4827  * things that need resilution 
4828  */
4829 bool MakeBase::getSubstitutions(const String &str, String &result)
4831     return getSubstitutionsRecursive(str, result, 0);
4836 /**
4837  * replace variable refs like ${a} with their values
4838  * Assume that the string has already been syntax validated
4839  */
4840 String MakeBase::eval(const String &s, const String &defaultVal)
4842     if (s.size()==0)
4843         return defaultVal;
4844     String ret;
4845     if (getSubstitutions(s, ret))
4846         return ret;
4847     else
4848         return defaultVal;
4852 /**
4853  * replace variable refs like ${a} with their values
4854  * return true or false
4855  * Assume that the string has already been syntax validated
4856  */
4857 bool MakeBase::evalBool(const String &s, bool defaultVal)
4859     if (s.size()==0)
4860         return defaultVal;
4861     String val = eval(s, "false");
4862     if (val.size()==0)
4863         return defaultVal;
4864     if (val == "true" || val == "TRUE")
4865         return true;
4866     else
4867         return false;
4871 /**
4872  * Get a string attribute, testing it for proper syntax and
4873  * property names.
4874  */
4875 bool MakeBase::getAttribute(Element *elem, const String &name,
4876                                     String &result)
4878     String s = elem->getAttribute(name);
4879     String tmp;
4880     bool ret = getSubstitutions(s, tmp);
4881     if (ret)
4882         result = s;  //assign -if- ok
4883     return ret;
4887 /**
4888  * Get a string value, testing it for proper syntax and
4889  * property names.
4890  */
4891 bool MakeBase::getValue(Element *elem, String &result)
4893     String s = elem->getValue();
4894     String tmp;
4895     bool ret = getSubstitutions(s, tmp);
4896     if (ret)
4897         result = s;  //assign -if- ok
4898     return ret;
4904 /**
4905  * Parse a <patternset> entry
4906  */  
4907 bool MakeBase::parsePatternSet(Element *elem,
4908                           MakeBase &propRef,
4909                           std::vector<String> &includes,
4910                           std::vector<String> &excludes
4911                           )
4913     std::vector<Element *> children  = elem->getChildren();
4914     for (unsigned int i=0 ; i<children.size() ; i++)
4915         {
4916         Element *child = children[i];
4917         String tagName = child->getName();
4918         if (tagName == "exclude")
4919             {
4920             String fname;
4921             if (!propRef.getAttribute(child, "name", fname))
4922                 return false;
4923             //trace("EXCLUDE: %s", fname.c_str());
4924             excludes.push_back(fname);
4925             }
4926         else if (tagName == "include")
4927             {
4928             String fname;
4929             if (!propRef.getAttribute(child, "name", fname))
4930                 return false;
4931             //trace("INCLUDE: %s", fname.c_str());
4932             includes.push_back(fname);
4933             }
4934         }
4936     return true;
4942 /**
4943  * Parse a <fileset> entry, and determine which files
4944  * should be included
4945  */  
4946 bool MakeBase::parseFileSet(Element *elem,
4947                           MakeBase &propRef,
4948                           FileSet &fileSet)
4950     String name = elem->getName();
4951     if (name != "fileset")
4952         {
4953         error("expected <fileset>");
4954         return false;
4955         }
4958     std::vector<String> includes;
4959     std::vector<String> excludes;
4961     //A fileset has one implied patternset
4962     if (!parsePatternSet(elem, propRef, includes, excludes))
4963         {
4964         return false;
4965         }
4966     //Look for child tags, including more patternsets
4967     std::vector<Element *> children  = elem->getChildren();
4968     for (unsigned int i=0 ; i<children.size() ; i++)
4969         {
4970         Element *child = children[i];
4971         String tagName = child->getName();
4972         if (tagName == "patternset")
4973             {
4974             if (!parsePatternSet(child, propRef, includes, excludes))
4975                 {
4976                 return false;
4977                 }
4978             }
4979         }
4981     String dir;
4982     //Now do the stuff
4983     //Get the base directory for reading file names
4984     if (!propRef.getAttribute(elem, "dir", dir))
4985         return false;
4987     fileSet.setDirectory(dir);
4988     fileSet.setIncludes(includes);
4989     fileSet.setExcludes(excludes);
4990     
4991     /*
4992     std::vector<String> fileList;
4993     if (dir.size() > 0)
4994         {
4995         String baseDir = propRef.resolve(dir);
4996         if (!listFiles(baseDir, "", includes, excludes, fileList))
4997             return false;
4998         }
4999     std::sort(fileList.begin(), fileList.end());
5000     result = fileList;
5001     */
5003     
5004     /*
5005     for (unsigned int i=0 ; i<result.size() ; i++)
5006         {
5007         trace("RES:%s", result[i].c_str());
5008         }
5009     */
5011     
5012     return true;
5015 /**
5016  * Parse a <filelist> entry.  This is far simpler than FileSet,
5017  * since no directory scanning is needed.  The file names are listed
5018  * explicitly.
5019  */  
5020 bool MakeBase::parseFileList(Element *elem,
5021                           MakeBase &propRef,
5022                           FileList &fileList)
5024     std::vector<String> fnames;
5025     //Look for child tags, namely "file"
5026     std::vector<Element *> children  = elem->getChildren();
5027     for (unsigned int i=0 ; i<children.size() ; i++)
5028         {
5029         Element *child = children[i];
5030         String tagName = child->getName();
5031         if (tagName == "file")
5032             {
5033             String fname = child->getAttribute("name");
5034             if (fname.size()==0)
5035                 {
5036                 error("<file> element requires name="" attribute");
5037                 return false;
5038                 }
5039             fnames.push_back(fname);
5040             }
5041         else
5042             {
5043             error("tag <%s> not allowed in <fileset>", tagName.c_str());
5044             return false;
5045             }
5046         }
5048     String dir;
5049     //Get the base directory for reading file names
5050     if (!propRef.getAttribute(elem, "dir", dir))
5051         return false;
5052     fileList.setDirectory(dir);
5053     fileList.setFiles(fnames);
5055     return true;
5060 /**
5061  * Create a directory, making intermediate dirs
5062  * if necessary
5063  */                  
5064 bool MakeBase::createDirectory(const String &dirname)
5066     //trace("## createDirectory: %s", dirname.c_str());
5067     //## first check if it exists
5068     struct stat finfo;
5069     String nativeDir = getNativePath(dirname);
5070     char *cnative = (char *) nativeDir.c_str();
5071 #ifdef __WIN32__
5072     if (strlen(cnative)==2 && cnative[1]==':')
5073         return true;
5074 #endif
5075     if (cachedStat(nativeDir, &finfo)==0)
5076         {
5077         if (!S_ISDIR(finfo.st_mode))
5078             {
5079             error("mkdir: file %s exists but is not a directory",
5080                   cnative);
5081             return false;
5082             }
5083         else //exists
5084             {
5085             return true;
5086             }
5087         }
5089     //## 2: pull off the last path segment, if any,
5090     //## to make the dir 'above' this one, if necessary
5091     unsigned int pos = dirname.find_last_of('/');
5092     if (pos>0 && pos != dirname.npos)
5093         {
5094         String subpath = dirname.substr(0, pos);
5095         //A letter root (c:) ?
5096         if (!createDirectory(subpath))
5097             return false;
5098         }
5099         
5100     //## 3: now make
5101 #ifdef __WIN32__
5102     if (mkdir(cnative)<0)
5103 #else
5104     if (mkdir(cnative, S_IRWXU | S_IRWXG | S_IRWXO)<0)
5105 #endif
5106         {
5107         error("cannot make directory '%s' : %s",
5108                  cnative, strerror(errno));
5109         return false;
5110         }
5112     removeFromStatCache(nativeDir);
5113         
5114     return true;
5118 /**
5119  * Remove a directory recursively
5120  */ 
5121 bool MakeBase::removeDirectory(const String &dirName)
5123     char *dname = (char *)dirName.c_str();
5125     DIR *dir = opendir(dname);
5126     if (!dir)
5127         {
5128         //# Let this fail nicely.
5129         return true;
5130         //error("error opening directory %s : %s", dname, strerror(errno));
5131         //return false;
5132         }
5133     
5134     while (true)
5135         {
5136         struct dirent *de = readdir(dir);
5137         if (!de)
5138             break;
5140         //Get the directory member name
5141         String s = de->d_name;
5142         if (s.size() == 0 || s[0] == '.')
5143             continue;
5144         String childName;
5145         if (dirName.size() > 0)
5146             {
5147             childName.append(dirName);
5148             childName.append("/");
5149             }
5150         childName.append(s);
5153         struct stat finfo;
5154         String childNative = getNativePath(childName);
5155         char *cnative = (char *)childNative.c_str();
5156         if (cachedStat(childNative, &finfo)<0)
5157             {
5158             error("cannot stat file:%s", cnative);
5159             }
5160         else if (S_ISDIR(finfo.st_mode))
5161             {
5162             //trace("DEL dir: %s", childName.c_str());
5163             if (!removeDirectory(childName))
5164                 {
5165                 return false;
5166                 }
5167             }
5168         else if (!S_ISREG(finfo.st_mode))
5169             {
5170             //trace("not regular: %s", cnative);
5171             }
5172         else
5173             {
5174             //trace("DEL file: %s", childName.c_str());
5175             if (!removeFile(childName))
5176                 {
5177                 return false;
5178                 }
5179             }
5180         }
5181     closedir(dir);
5183     //Now delete the directory
5184     String native = getNativePath(dirName);
5185     if (rmdir(native.c_str())<0)
5186         {
5187         error("could not delete directory %s : %s",
5188             native.c_str() , strerror(errno));
5189         return false;
5190         }
5192     removeFromStatCache(native);
5194     return true;
5195     
5199 /**
5200  * Copy a file from one name to another. Perform only if needed
5201  */ 
5202 bool MakeBase::copyFile(const String &srcFile, const String &destFile)
5204     //# 1 Check up-to-date times
5205     String srcNative = getNativePath(srcFile);
5206     struct stat srcinfo;
5207     if (cachedStat(srcNative, &srcinfo)<0)
5208         {
5209         error("source file %s for copy does not exist",
5210                  srcNative.c_str());
5211         return false;
5212         }
5214     String destNative = getNativePath(destFile);
5215     struct stat destinfo;
5216     if (cachedStat(destNative, &destinfo)==0)
5217         {
5218         if (destinfo.st_mtime >= srcinfo.st_mtime)
5219             return true;
5220         }
5221         
5222     //# 2 prepare a destination directory if necessary
5223     unsigned int pos = destFile.find_last_of('/');
5224     if (pos != destFile.npos)
5225         {
5226         String subpath = destFile.substr(0, pos);
5227         if (!createDirectory(subpath))
5228             return false;
5229         }
5231     //# 3 do the data copy
5232 #ifndef __WIN32__
5234     FILE *srcf = fopen(srcNative.c_str(), "rb");
5235     if (!srcf)
5236         {
5237         error("copyFile cannot open '%s' for reading", srcNative.c_str());
5238         return false;
5239         }
5240     FILE *destf = fopen(destNative.c_str(), "wb");
5241     if (!destf)
5242         {
5243         fclose(srcf);
5244         error("copyFile cannot open %s for writing", srcNative.c_str());
5245         return false;
5246         }
5248     while (!feof(srcf))
5249         {
5250         int ch = fgetc(srcf);
5251         if (ch<0)
5252             break;
5253         fputc(ch, destf);
5254         }
5256     fclose(destf);
5257     fclose(srcf);
5259 #else
5260     
5261     if (!CopyFile(srcNative.c_str(), destNative.c_str(), false))
5262         {
5263         error("copyFile from %s to %s failed",
5264              srcNative.c_str(), destNative.c_str());
5265         return false;
5266         }
5267         
5268 #endif /* __WIN32__ */
5270     removeFromStatCache(destNative);
5272     return true;
5276 /**
5277  * Delete a file
5278  */ 
5279 bool MakeBase::removeFile(const String &file)
5281     String native = getNativePath(file);
5283     if (!fileExists(native))
5284         {
5285         return true;
5286         }
5288 #ifdef WIN32
5289     // On Windows 'remove' will only delete files
5291     if (remove(native.c_str())<0)
5292         {
5293         if (errno==EACCES)
5294             {
5295             error("File %s is read-only", native.c_str());
5296             }
5297         else if (errno==ENOENT)
5298             {
5299             error("File %s does not exist or is a directory", native.c_str());
5300             }
5301         else
5302             {
5303             error("Failed to delete file %s: %s", native.c_str(), strerror(errno));
5304             }
5305         return false;
5306         }
5308 #else
5310     if (!isRegularFile(native))
5311         {
5312         error("File %s does not exist or is not a regular file", native.c_str());
5313         return false;
5314         }
5316     if (remove(native.c_str())<0)
5317         {
5318         if (errno==EACCES)
5319             {
5320             error("File %s is read-only", native.c_str());
5321             }
5322         else
5323             {
5324             error(
5325                 errno==EACCES ? "File %s is read-only" :
5326                 errno==ENOENT ? "File %s does not exist or is a directory" :
5327                 "Failed to delete file %s: %s", native.c_str());
5328             }
5329         return false;
5330         }
5332 #endif
5334     removeFromStatCache(native);
5336     return true;
5340 /**
5341  * Tests if the file exists
5342  */ 
5343 bool MakeBase::fileExists(const String &fileName)
5345     String native = getNativePath(fileName);
5346     struct stat finfo;
5347     
5348     //Exists?
5349     if (cachedStat(native, &finfo)<0)
5350         return false;
5352     return true;
5356 /**
5357  * Tests if the file exists and is a regular file
5358  */ 
5359 bool MakeBase::isRegularFile(const String &fileName)
5361     String native = getNativePath(fileName);
5362     struct stat finfo;
5363     
5364     //Exists?
5365     if (cachedStat(native, &finfo)<0)
5366         return false;
5369     //check the file mode
5370     if (!S_ISREG(finfo.st_mode))
5371         return false;
5373     return true;
5376 /**
5377  * Tests if the file exists and is a directory
5378  */ 
5379 bool MakeBase::isDirectory(const String &fileName)
5381     String native = getNativePath(fileName);
5382     struct stat finfo;
5383     
5384     //Exists?
5385     if (cachedStat(native, &finfo)<0)
5386         return false;
5389     //check the file mode
5390     if (!S_ISDIR(finfo.st_mode))
5391         return false;
5393     return true;
5398 /**
5399  * Tests is the modification of fileA is newer than fileB
5400  */ 
5401 bool MakeBase::isNewerThan(const String &fileA, const String &fileB)
5403     //trace("isNewerThan:'%s' , '%s'", fileA.c_str(), fileB.c_str());
5404     String nativeA = getNativePath(fileA);
5405     struct stat infoA;
5406     //IF source does not exist, NOT newer
5407     if (cachedStat(nativeA, &infoA)<0)
5408         {
5409         return false;
5410         }
5412     String nativeB = getNativePath(fileB);
5413     struct stat infoB;
5414     //IF dest does not exist, YES, newer
5415     if (cachedStat(nativeB, &infoB)<0)
5416         {
5417         return true;
5418         }
5420     //check the actual times
5421     if (infoA.st_mtime > infoB.st_mtime)
5422         {
5423         return true;
5424         }
5426     return false;
5430 //########################################################################
5431 //# P K G    C O N F I G
5432 //########################################################################
5435 /**
5436  * Get a character from the buffer at pos.  If out of range,
5437  * return -1 for safety
5438  */
5439 int PkgConfig::get(int pos)
5441     if (pos>parselen)
5442         return -1;
5443     return parsebuf[pos];
5448 /**
5449  *  Skip over all whitespace characters beginning at pos.  Return
5450  *  the position of the first non-whitespace character.
5451  *  Pkg-config is line-oriented, so check for newline
5452  */
5453 int PkgConfig::skipwhite(int pos)
5455     while (pos < parselen)
5456         {
5457         int ch = get(pos);
5458         if (ch < 0)
5459             break;
5460         if (!isspace(ch))
5461             break;
5462         pos++;
5463         }
5464     return pos;
5468 /**
5469  *  Parse the buffer beginning at pos, for a word.  Fill
5470  *  'ret' with the result.  Return the position after the
5471  *  word.
5472  */
5473 int PkgConfig::getword(int pos, String &ret)
5475     while (pos < parselen)
5476         {
5477         int ch = get(pos);
5478         if (ch < 0)
5479             break;
5480         if (!isalnum(ch) && ch != '_' && ch != '-' && ch != '+' && ch != '.')
5481             break;
5482         ret.push_back((char)ch);
5483         pos++;
5484         }
5485     return pos;
5488 bool PkgConfig::parseRequires()
5490     if (requires.size() == 0)
5491         return true;
5492     parsebuf = (char *)requires.c_str();
5493     parselen = requires.size();
5494     int pos = 0;
5495     while (pos < parselen)
5496         {
5497         pos = skipwhite(pos);
5498         String val;
5499         int pos2 = getword(pos, val);
5500         if (pos2 == pos)
5501             break;
5502         pos = pos2;
5503         //trace("val %s", val.c_str());
5504         requireList.push_back(val);
5505         }
5506     return true;
5510 static int getint(const String str)
5512     char *s = (char *)str.c_str();
5513     char *ends = NULL;
5514     long val = strtol(s, &ends, 10);
5515     if (ends == s)
5516         return 0L;
5517     else
5518         return val;
5521 void PkgConfig::parseVersion()
5523     if (version.size() == 0)
5524         return;
5525     String s1, s2, s3;
5526     unsigned int pos = 0;
5527     unsigned int pos2 = version.find('.', pos);
5528     if (pos2 == version.npos)
5529         {
5530         s1 = version;
5531         }
5532     else
5533         {
5534         s1 = version.substr(pos, pos2-pos);
5535         pos = pos2;
5536         pos++;
5537         if (pos < version.size())
5538             {
5539             pos2 = version.find('.', pos);
5540             if (pos2 == version.npos)
5541                 {
5542                 s2 = version.substr(pos, version.size()-pos);
5543                 }
5544             else
5545                 {
5546                 s2 = version.substr(pos, pos2-pos);
5547                 pos = pos2;
5548                 pos++;
5549                 if (pos < version.size())
5550                     s3 = version.substr(pos, pos2-pos);
5551                 }
5552             }
5553         }
5555     majorVersion = getint(s1);
5556     minorVersion = getint(s2);
5557     microVersion = getint(s3);
5558     //trace("version:%d.%d.%d", majorVersion,
5559     //          minorVersion, microVersion );
5563 bool PkgConfig::parseLine(const String &lineBuf)
5565     parsebuf = (char *)lineBuf.c_str();
5566     parselen = lineBuf.size();
5567     int pos = 0;
5568     
5569     while (pos < parselen)
5570         {
5571         String attrName;
5572         pos = skipwhite(pos);
5573         int ch = get(pos);
5574         if (ch == '#')
5575             {
5576             //comment.  eat the rest of the line
5577             while (pos < parselen)
5578                 {
5579                 ch = get(pos);
5580                 if (ch == '\n' || ch < 0)
5581                     break;
5582                 pos++;
5583                 }
5584             continue;
5585             }
5586         pos = getword(pos, attrName);
5587         if (attrName.size() == 0)
5588             continue;
5589         
5590         pos = skipwhite(pos);
5591         ch = get(pos);
5592         if (ch != ':' && ch != '=')
5593             {
5594             error("expected ':' or '='");
5595             return false;
5596             }
5597         pos++;
5598         pos = skipwhite(pos);
5599         String attrVal;
5600         while (pos < parselen)
5601             {
5602             ch = get(pos);
5603             if (ch == '\n' || ch < 0)
5604                 break;
5605             else if (ch == '$' && get(pos+1) == '{')
5606                 {
5607                 //#  this is a ${substitution}
5608                 pos += 2;
5609                 String subName;
5610                 while (pos < parselen)
5611                     {
5612                     ch = get(pos);
5613                     if (ch < 0)
5614                         {
5615                         error("unterminated substitution");
5616                         return false;
5617                         }
5618                     else if (ch == '}')
5619                         break;
5620                     else
5621                         subName.push_back((char)ch);
5622                     pos++;
5623                     }
5624                 //trace("subName:%s %s", subName.c_str(), prefix.c_str());
5625                 if (subName == "prefix" && prefix.size()>0)
5626                     {
5627                     attrVal.append(prefix);
5628                     //trace("prefix override:%s", prefix.c_str());
5629                     }
5630                 else
5631                     {
5632                     String subVal = attrs[subName];
5633                     //trace("subVal:%s", subVal.c_str());
5634                     attrVal.append(subVal);
5635                     }
5636                 }
5637             else
5638                 attrVal.push_back((char)ch);
5639             pos++;
5640             }
5642         attrVal = trim(attrVal);
5643         attrs[attrName] = attrVal;
5645         String attrNameL = toLower(attrName);
5647         if (attrNameL == "name")
5648             name = attrVal;
5649         else if (attrNameL == "description")
5650             description = attrVal;
5651         else if (attrNameL == "cflags")
5652             cflags = attrVal;
5653         else if (attrNameL == "libs")
5654             libs = attrVal;
5655         else if (attrNameL == "requires")
5656             requires = attrVal;
5657         else if (attrNameL == "version")
5658             version = attrVal;
5660         //trace("name:'%s'  value:'%s'",
5661         //      attrName.c_str(), attrVal.c_str());
5662         }
5664     return true;
5668 bool PkgConfig::parse(const String &buf)
5670     init();
5672     String line;
5673     int lineNr = 0;
5674     for (unsigned int p=0 ; p<buf.size() ; p++)
5675         {
5676         int ch = buf[p];
5677         if (ch == '\n' || ch == '\r')
5678             {
5679             if (!parseLine(line))
5680                 return false;
5681             line.clear();
5682             lineNr++;
5683             }
5684         else
5685             {
5686             line.push_back(ch);
5687             }
5688         }
5689     if (line.size()>0)
5690         {
5691         if (!parseLine(line))
5692             return false;
5693         }
5695     parseRequires();
5696     parseVersion();
5698     return true;
5704 void PkgConfig::dumpAttrs()
5706     //trace("### PkgConfig attributes for %s", fileName.c_str());
5707     std::map<String, String>::iterator iter;
5708     for (iter=attrs.begin() ; iter!=attrs.end() ; iter++)
5709         {
5710         trace("   %s = %s", iter->first.c_str(), iter->second.c_str());
5711         }
5715 bool PkgConfig::readFile(const String &fname)
5717     fileName = getNativePath(fname);
5719     FILE *f = fopen(fileName.c_str(), "r");
5720     if (!f)
5721         {
5722         error("cannot open file '%s' for reading", fileName.c_str());
5723         return false;
5724         }
5725     String buf;
5726     while (true)
5727         {
5728         int ch = fgetc(f);
5729         if (ch < 0)
5730             break;
5731         buf.push_back((char)ch);
5732         }
5733     fclose(f);
5735     //trace("####### File:\n%s", buf.c_str());
5736     if (!parse(buf))
5737         {
5738         return false;
5739         }
5741     //dumpAttrs();
5743     return true;
5748 bool PkgConfig::query(const String &pkgName)
5750     name = pkgName;
5752     String fname = path;
5753     fname.append("/");
5754     fname.append(name);
5755     fname.append(".pc");
5757     if (!readFile(fname))
5758         {
5759         error("Cannot find package '%s'. Do you have it installed?",
5760                        pkgName.c_str());
5761         return false;
5762         }
5763     
5764     return true;
5768 //########################################################################
5769 //# D E P T O O L
5770 //########################################################################
5774 /**
5775  *  Class which holds information for each file.
5776  */
5777 class FileRec
5779 public:
5781     typedef enum
5782         {
5783         UNKNOWN,
5784         CFILE,
5785         HFILE,
5786         OFILE
5787         } FileType;
5789     /**
5790      *  Constructor
5791      */
5792     FileRec()
5793         { init(); type = UNKNOWN; }
5795     /**
5796      *  Copy constructor
5797      */
5798     FileRec(const FileRec &other)
5799         { init(); assign(other); }
5800     /**
5801      *  Constructor
5802      */
5803     FileRec(int typeVal)
5804         { init(); type = typeVal; }
5805     /**
5806      *  Assignment operator
5807      */
5808     FileRec &operator=(const FileRec &other)
5809         { init(); assign(other); return *this; }
5812     /**
5813      *  Destructor
5814      */
5815     ~FileRec()
5816         {}
5818     /**
5819      *  Directory part of the file name
5820      */
5821     String path;
5823     /**
5824      *  Base name, sans directory and suffix
5825      */
5826     String baseName;
5828     /**
5829      *  File extension, such as cpp or h
5830      */
5831     String suffix;
5833     /**
5834      *  Type of file: CFILE, HFILE, OFILE
5835      */
5836     int type;
5838     /**
5839      * Used to list files ref'd by this one
5840      */
5841     std::map<String, FileRec *> files;
5844 private:
5846     void init()
5847         {
5848         }
5850     void assign(const FileRec &other)
5851         {
5852         type     = other.type;
5853         baseName = other.baseName;
5854         suffix   = other.suffix;
5855         files    = other.files;
5856         }
5858 };
5862 /**
5863  *  Simpler dependency record
5864  */
5865 class DepRec
5867 public:
5869     /**
5870      *  Constructor
5871      */
5872     DepRec()
5873         {init();}
5875     /**
5876      *  Copy constructor
5877      */
5878     DepRec(const DepRec &other)
5879         {init(); assign(other);}
5880     /**
5881      *  Constructor
5882      */
5883     DepRec(const String &fname)
5884         {init(); name = fname; }
5885     /**
5886      *  Assignment operator
5887      */
5888     DepRec &operator=(const DepRec &other)
5889         {init(); assign(other); return *this;}
5892     /**
5893      *  Destructor
5894      */
5895     ~DepRec()
5896         {}
5898     /**
5899      *  Directory part of the file name
5900      */
5901     String path;
5903     /**
5904      *  Base name, without the path and suffix
5905      */
5906     String name;
5908     /**
5909      *  Suffix of the source
5910      */
5911     String suffix;
5914     /**
5915      * Used to list files ref'd by this one
5916      */
5917     std::vector<String> files;
5920 private:
5922     void init()
5923         {
5924         }
5926     void assign(const DepRec &other)
5927         {
5928         path     = other.path;
5929         name     = other.name;
5930         suffix   = other.suffix;
5931         files    = other.files; //avoid recursion
5932         }
5934 };
5937 class DepTool : public MakeBase
5939 public:
5941     /**
5942      *  Constructor
5943      */
5944     DepTool()
5945         { init(); }
5947     /**
5948      *  Copy constructor
5949      */
5950     DepTool(const DepTool &other)
5951         { init(); assign(other); }
5953     /**
5954      *  Assignment operator
5955      */
5956     DepTool &operator=(const DepTool &other)
5957         { init(); assign(other); return *this; }
5960     /**
5961      *  Destructor
5962      */
5963     ~DepTool()
5964         {}
5967     /**
5968      *  Reset this section of code
5969      */
5970     virtual void init();
5971     
5972     /**
5973      *  Reset this section of code
5974      */
5975     virtual void assign(const DepTool &other)
5976         {
5977         }
5978     
5979     /**
5980      *  Sets the source directory which will be scanned
5981      */
5982     virtual void setSourceDirectory(const String &val)
5983         { sourceDir = val; }
5985     /**
5986      *  Returns the source directory which will be scanned
5987      */
5988     virtual String getSourceDirectory()
5989         { return sourceDir; }
5991     /**
5992      *  Sets the list of files within the directory to analyze
5993      */
5994     virtual void setFileList(const std::vector<String> &list)
5995         { fileList = list; }
5997     /**
5998      * Creates the list of all file names which will be
5999      * candidates for further processing.  Reads make.exclude
6000      * to see which files for directories to leave out.
6001      */
6002     virtual bool createFileList();
6005     /**
6006      *  Generates the forward dependency list
6007      */
6008     virtual bool generateDependencies();
6011     /**
6012      *  Generates the forward dependency list, saving the file
6013      */
6014     virtual bool generateDependencies(const String &);
6017     /**
6018      *  Load a dependency file
6019      */
6020     std::vector<DepRec> loadDepFile(const String &fileName);
6022     /**
6023      *  Load a dependency file, generating one if necessary
6024      */
6025     std::vector<DepRec> getDepFile(const String &fileName,
6026               bool forceRefresh);
6028     /**
6029      *  Save a dependency file
6030      */
6031     bool saveDepFile(const String &fileName);
6034 private:
6037     /**
6038      *
6039      */
6040     void parseName(const String &fullname,
6041                    String &path,
6042                    String &basename,
6043                    String &suffix);
6045     /**
6046      *
6047      */
6048     int get(int pos);
6050     /**
6051      *
6052      */
6053     int skipwhite(int pos);
6055     /**
6056      *
6057      */
6058     int getword(int pos, String &ret);
6060     /**
6061      *
6062      */
6063     bool sequ(int pos, const char *key);
6065     /**
6066      *
6067      */
6068     bool addIncludeFile(FileRec *frec, const String &fname);
6070     /**
6071      *
6072      */
6073     bool scanFile(const String &fname, FileRec *frec);
6075     /**
6076      *
6077      */
6078     bool processDependency(FileRec *ofile, FileRec *include);
6080     /**
6081      *
6082      */
6083     String sourceDir;
6085     /**
6086      *
6087      */
6088     std::vector<String> fileList;
6090     /**
6091      *
6092      */
6093     std::vector<String> directories;
6095     /**
6096      * A list of all files which will be processed for
6097      * dependencies.
6098      */
6099     std::map<String, FileRec *> allFiles;
6101     /**
6102      * The list of .o files, and the
6103      * dependencies upon them.
6104      */
6105     std::map<String, FileRec *> oFiles;
6107     int depFileSize;
6108     char *depFileBuf;
6110     static const int readBufSize = 8192;
6111     char readBuf[8193];//byte larger
6113 };
6119 /**
6120  *  Clean up after processing.  Called by the destructor, but should
6121  *  also be called before the object is reused.
6122  */
6123 void DepTool::init()
6125     sourceDir = ".";
6127     fileList.clear();
6128     directories.clear();
6129     
6130     //clear output file list
6131     std::map<String, FileRec *>::iterator iter;
6132     for (iter=oFiles.begin(); iter!=oFiles.end() ; iter++)
6133         delete iter->second;
6134     oFiles.clear();
6136     //allFiles actually contains the master copies. delete them
6137     for (iter= allFiles.begin(); iter!=allFiles.end() ; iter++)
6138         delete iter->second;
6139     allFiles.clear(); 
6146 /**
6147  *  Parse a full path name into path, base name, and suffix
6148  */
6149 void DepTool::parseName(const String &fullname,
6150                         String &path,
6151                         String &basename,
6152                         String &suffix)
6154     if (fullname.size() < 2)
6155         return;
6157     unsigned int pos = fullname.find_last_of('/');
6158     if (pos != fullname.npos && pos<fullname.size()-1)
6159         {
6160         path = fullname.substr(0, pos);
6161         pos++;
6162         basename = fullname.substr(pos, fullname.size()-pos);
6163         }
6164     else
6165         {
6166         path = "";
6167         basename = fullname;
6168         }
6170     pos = basename.find_last_of('.');
6171     if (pos != basename.npos && pos<basename.size()-1)
6172         {
6173         suffix   = basename.substr(pos+1, basename.size()-pos-1);
6174         basename = basename.substr(0, pos);
6175         }
6177     //trace("parsename:%s %s %s", path.c_str(),
6178     //        basename.c_str(), suffix.c_str()); 
6183 /**
6184  *  Generate our internal file list.
6185  */
6186 bool DepTool::createFileList()
6189     for (unsigned int i=0 ; i<fileList.size() ; i++)
6190         {
6191         String fileName = fileList[i];
6192         //trace("## FileName:%s", fileName.c_str());
6193         String path;
6194         String basename;
6195         String sfx;
6196         parseName(fileName, path, basename, sfx);
6197         if (sfx == "cpp" || sfx == "c" || sfx == "cxx"   ||
6198             sfx == "cc" || sfx == "CC")
6199             {
6200             FileRec *fe         = new FileRec(FileRec::CFILE);
6201             fe->path            = path;
6202             fe->baseName        = basename;
6203             fe->suffix          = sfx;
6204             allFiles[fileName]  = fe;
6205             }
6206         else if (sfx == "h"   ||  sfx == "hh"  ||
6207                  sfx == "hpp" ||  sfx == "hxx")
6208             {
6209             FileRec *fe         = new FileRec(FileRec::HFILE);
6210             fe->path            = path;
6211             fe->baseName        = basename;
6212             fe->suffix          = sfx;
6213             allFiles[fileName]  = fe;
6214             }
6215         }
6217     if (!listDirectories(sourceDir, "", directories))
6218         return false;
6219         
6220     return true;
6227 /**
6228  * Get a character from the buffer at pos.  If out of range,
6229  * return -1 for safety
6230  */
6231 int DepTool::get(int pos)
6233     if (pos>depFileSize)
6234         return -1;
6235     return depFileBuf[pos];
6240 /**
6241  *  Skip over all whitespace characters beginning at pos.  Return
6242  *  the position of the first non-whitespace character.
6243  */
6244 int DepTool::skipwhite(int pos)
6246     while (pos < depFileSize)
6247         {
6248         int ch = get(pos);
6249         if (ch < 0)
6250             break;
6251         if (!isspace(ch))
6252             break;
6253         pos++;
6254         }
6255     return pos;
6259 /**
6260  *  Parse the buffer beginning at pos, for a word.  Fill
6261  *  'ret' with the result.  Return the position after the
6262  *  word.
6263  */
6264 int DepTool::getword(int pos, String &ret)
6266     while (pos < depFileSize)
6267         {
6268         int ch = get(pos);
6269         if (ch < 0)
6270             break;
6271         if (isspace(ch))
6272             break;
6273         ret.push_back((char)ch);
6274         pos++;
6275         }
6276     return pos;
6279 /**
6280  * Return whether the sequence of characters in the buffer
6281  * beginning at pos match the key,  for the length of the key
6282  */
6283 bool DepTool::sequ(int pos, const char *key)
6285     while (*key)
6286         {
6287         if (*key != get(pos))
6288             return false;
6289         key++; pos++;
6290         }
6291     return true;
6296 /**
6297  *  Add an include file name to a file record.  If the name
6298  *  is not found in allFiles explicitly, try prepending include
6299  *  directory names to it and try again.
6300  */
6301 bool DepTool::addIncludeFile(FileRec *frec, const String &iname)
6303     //# if the name is an exact match to a path name
6304     //# in allFiles, like "myinc.h"
6305     std::map<String, FileRec *>::iterator iter =
6306            allFiles.find(iname);
6307     if (iter != allFiles.end()) //already exists
6308         {
6309          //h file in same dir
6310         FileRec *other = iter->second;
6311         //trace("local: '%s'", iname.c_str());
6312         frec->files[iname] = other;
6313         return true;
6314         }
6315     else 
6316         {
6317         //## Ok, it was not found directly
6318         //look in other dirs
6319         std::vector<String>::iterator diter;
6320         for (diter=directories.begin() ;
6321              diter!=directories.end() ; diter++)
6322             {
6323             String dfname = *diter;
6324             dfname.append("/");
6325             dfname.append(iname);
6326             URI fullPathURI(dfname);  //normalize path name
6327             String fullPath = fullPathURI.getPath();
6328             if (fullPath[0] == '/')
6329                 fullPath = fullPath.substr(1);
6330             //trace("Normalized %s to %s", dfname.c_str(), fullPath.c_str());
6331             iter = allFiles.find(fullPath);
6332             if (iter != allFiles.end())
6333                 {
6334                 FileRec *other = iter->second;
6335                 //trace("other: '%s'", iname.c_str());
6336                 frec->files[fullPath] = other;
6337                 return true;
6338                 }
6339             }
6340         }
6341     return true;
6346 /**
6347  *  Lightly parse a file to find the #include directives.  Do
6348  *  a bit of state machine stuff to make sure that the directive
6349  *  is valid.  (Like not in a comment).
6350  */
6351 bool DepTool::scanFile(const String &fname, FileRec *frec)
6353     String fileName;
6354     if (sourceDir.size() > 0)
6355         {
6356         fileName.append(sourceDir);
6357         fileName.append("/");
6358         }
6359     fileName.append(fname);
6360     String nativeName = getNativePath(fileName);
6361     FILE *f = fopen(nativeName.c_str(), "r");
6362     if (!f)
6363         {
6364         error("Could not open '%s' for reading", fname.c_str());
6365         return false;
6366         }
6367     String buf;
6368     while (!feof(f))
6369         {
6370         int nrbytes = fread(readBuf, 1, readBufSize, f);
6371         readBuf[nrbytes] = '\0';
6372         buf.append(readBuf);
6373         }
6374     fclose(f);
6376     depFileSize = buf.size();
6377     depFileBuf  = (char *)buf.c_str();
6378     int pos = 0;
6381     while (pos < depFileSize)
6382         {
6383         //trace("p:%c", get(pos));
6385         //# Block comment
6386         if (get(pos) == '/' && get(pos+1) == '*')
6387             {
6388             pos += 2;
6389             while (pos < depFileSize)
6390                 {
6391                 if (get(pos) == '*' && get(pos+1) == '/')
6392                     {
6393                     pos += 2;
6394                     break;
6395                     }
6396                 else
6397                     pos++;
6398                 }
6399             }
6400         //# Line comment
6401         else if (get(pos) == '/' && get(pos+1) == '/')
6402             {
6403             pos += 2;
6404             while (pos < depFileSize)
6405                 {
6406                 if (get(pos) == '\n')
6407                     {
6408                     pos++;
6409                     break;
6410                     }
6411                 else
6412                     pos++;
6413                 }
6414             }
6415         //# #include! yaay
6416         else if (sequ(pos, "#include"))
6417             {
6418             pos += 8;
6419             pos = skipwhite(pos);
6420             String iname;
6421             pos = getword(pos, iname);
6422             if (iname.size()>2)
6423                 {
6424                 iname = iname.substr(1, iname.size()-2);
6425                 addIncludeFile(frec, iname);
6426                 }
6427             }
6428         else
6429             {
6430             pos++;
6431             }
6432         }
6434     return true;
6439 /**
6440  *  Recursively check include lists to find all files in allFiles to which
6441  *  a given file is dependent.
6442  */
6443 bool DepTool::processDependency(FileRec *ofile, FileRec *include)
6445     std::map<String, FileRec *>::iterator iter;
6446     for (iter=include->files.begin() ; iter!=include->files.end() ; iter++)
6447         {
6448         String fname  = iter->first;
6449         if (ofile->files.find(fname) != ofile->files.end())
6450             {
6451             //trace("file '%s' already seen", fname.c_str());
6452             continue;
6453             }
6454         FileRec *child  = iter->second;
6455         ofile->files[fname] = child;
6456       
6457         processDependency(ofile, child);
6458         }
6461     return true;
6468 /**
6469  *  Generate the file dependency list.
6470  */
6471 bool DepTool::generateDependencies()
6473     std::map<String, FileRec *>::iterator iter;
6474     //# First pass.  Scan for all includes
6475     for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
6476         {
6477         FileRec *frec = iter->second;
6478         if (!scanFile(iter->first, frec))
6479             {
6480             //quit?
6481             }
6482         }
6484     //# Second pass.  Scan for all includes
6485     for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
6486         {
6487         FileRec *include = iter->second;
6488         if (include->type == FileRec::CFILE)
6489             {
6490             //String cFileName   = iter->first;
6491             FileRec *ofile     = new FileRec(FileRec::OFILE);
6492             ofile->path        = include->path;
6493             ofile->baseName    = include->baseName;
6494             ofile->suffix      = include->suffix;
6495             String fname       = include->path;
6496             if (fname.size()>0)
6497                 fname.append("/");
6498             fname.append(include->baseName);
6499             fname.append(".o");
6500             oFiles[fname]    = ofile;
6501             //add the .c file first?   no, don't
6502             //ofile->files[cFileName] = include;
6503             
6504             //trace("ofile:%s", fname.c_str());
6506             processDependency(ofile, include);
6507             }
6508         }
6510       
6511     return true;
6516 /**
6517  *  High-level call to generate deps and optionally save them
6518  */
6519 bool DepTool::generateDependencies(const String &fileName)
6521     if (!createFileList())
6522         return false;
6523     if (!generateDependencies())
6524         return false;
6525     if (!saveDepFile(fileName))
6526         return false;
6527     return true;
6531 /**
6532  *   This saves the dependency cache.
6533  */
6534 bool DepTool::saveDepFile(const String &fileName)
6536     time_t tim;
6537     time(&tim);
6539     FILE *f = fopen(fileName.c_str(), "w");
6540     if (!f)
6541         {
6542         trace("cannot open '%s' for writing", fileName.c_str());
6543         }
6544     fprintf(f, "<?xml version='1.0'?>\n");
6545     fprintf(f, "<!--\n");
6546     fprintf(f, "########################################################\n");
6547     fprintf(f, "## File: build.dep\n");
6548     fprintf(f, "## Generated by BuildTool at :%s", ctime(&tim));
6549     fprintf(f, "########################################################\n");
6550     fprintf(f, "-->\n");
6552     fprintf(f, "<dependencies source='%s'>\n\n", sourceDir.c_str());
6553     std::map<String, FileRec *>::iterator iter;
6554     for (iter=oFiles.begin() ; iter!=oFiles.end() ; iter++)
6555         {
6556         FileRec *frec = iter->second;
6557         if (frec->type == FileRec::OFILE)
6558             {
6559             fprintf(f, "<object path='%s' name='%s' suffix='%s'>\n",
6560                  frec->path.c_str(), frec->baseName.c_str(), frec->suffix.c_str());
6561             std::map<String, FileRec *>::iterator citer;
6562             for (citer=frec->files.begin() ; citer!=frec->files.end() ; citer++)
6563                 {
6564                 String cfname = citer->first;
6565                 fprintf(f, "    <dep name='%s'/>\n", cfname.c_str());
6566                 }
6567             fprintf(f, "</object>\n\n");
6568             }
6569         }
6571     fprintf(f, "</dependencies>\n");
6572     fprintf(f, "\n");
6573     fprintf(f, "<!--\n");
6574     fprintf(f, "########################################################\n");
6575     fprintf(f, "## E N D\n");
6576     fprintf(f, "########################################################\n");
6577     fprintf(f, "-->\n");
6579     fclose(f);
6581     return true;
6587 /**
6588  *   This loads the dependency cache.
6589  */
6590 std::vector<DepRec> DepTool::loadDepFile(const String &depFile)
6592     std::vector<DepRec> result;
6593     
6594     Parser parser;
6595     Element *root = parser.parseFile(depFile.c_str());
6596     if (!root)
6597         {
6598         //error("Could not open %s for reading", depFile.c_str());
6599         return result;
6600         }
6602     if (root->getChildren().size()==0 ||
6603         root->getChildren()[0]->getName()!="dependencies")
6604         {
6605         error("loadDepFile: main xml element should be <dependencies>");
6606         delete root;
6607         return result;
6608         }
6610     //########## Start parsing
6611     Element *depList = root->getChildren()[0];
6613     std::vector<Element *> objects = depList->getChildren();
6614     for (unsigned int i=0 ; i<objects.size() ; i++)
6615         {
6616         Element *objectElem = objects[i];
6617         String tagName = objectElem->getName();
6618         if (tagName != "object")
6619             {
6620             error("loadDepFile: <dependencies> should have only <object> children");
6621             return result;
6622             }
6624         String objName   = objectElem->getAttribute("name");
6625          //trace("object:%s", objName.c_str());
6626         DepRec depObject(objName);
6627         depObject.path   = objectElem->getAttribute("path");
6628         depObject.suffix = objectElem->getAttribute("suffix");
6629         //########## DESCRIPTION
6630         std::vector<Element *> depElems = objectElem->getChildren();
6631         for (unsigned int i=0 ; i<depElems.size() ; i++)
6632             {
6633             Element *depElem = depElems[i];
6634             tagName = depElem->getName();
6635             if (tagName != "dep")
6636                 {
6637                 error("loadDepFile: <object> should have only <dep> children");
6638                 return result;
6639                 }
6640             String depName = depElem->getAttribute("name");
6641             //trace("    dep:%s", depName.c_str());
6642             depObject.files.push_back(depName);
6643             }
6645         //Insert into the result list, in a sorted manner
6646         bool inserted = false;
6647         std::vector<DepRec>::iterator iter;
6648         for (iter = result.begin() ; iter != result.end() ; iter++)
6649             {
6650             String vpath = iter->path;
6651             vpath.append("/");
6652             vpath.append(iter->name);
6653             String opath = depObject.path;
6654             opath.append("/");
6655             opath.append(depObject.name);
6656             if (vpath > opath)
6657                 {
6658                 inserted = true;
6659                 iter = result.insert(iter, depObject);
6660                 break;
6661                 }
6662             }
6663         if (!inserted)
6664             result.push_back(depObject);
6665         }
6667     delete root;
6669     return result;
6673 /**
6674  *   This loads the dependency cache.
6675  */
6676 std::vector<DepRec> DepTool::getDepFile(const String &depFile,
6677                    bool forceRefresh)
6679     std::vector<DepRec> result;
6680     if (forceRefresh)
6681         {
6682         generateDependencies(depFile);
6683         result = loadDepFile(depFile);
6684         }
6685     else
6686         {
6687         //try once
6688         result = loadDepFile(depFile);
6689         if (result.size() == 0)
6690             {
6691             //fail? try again
6692             generateDependencies(depFile);
6693             result = loadDepFile(depFile);
6694             }
6695         }
6696     return result;
6702 //########################################################################
6703 //# T A S K
6704 //########################################################################
6705 //forward decl
6706 class Target;
6707 class Make;
6709 /**
6710  *
6711  */
6712 class Task : public MakeBase
6715 public:
6717     typedef enum
6718         {
6719         TASK_NONE,
6720         TASK_CC,
6721         TASK_COPY,
6722         TASK_CXXTEST_PART,
6723         TASK_CXXTEST_ROOT,
6724         TASK_CXXTEST_RUN,
6725         TASK_DELETE,
6726         TASK_ECHO,
6727         TASK_JAR,
6728         TASK_JAVAC,
6729         TASK_LINK,
6730         TASK_MAKEFILE,
6731         TASK_MKDIR,
6732         TASK_MSGFMT,
6733         TASK_PKG_CONFIG,
6734         TASK_RANLIB,
6735         TASK_RC,
6736         TASK_SHAREDLIB,
6737         TASK_STATICLIB,
6738         TASK_STRIP,
6739         TASK_TOUCH,
6740         TASK_TSTAMP
6741         } TaskType;
6742         
6744     /**
6745      *
6746      */
6747     Task(MakeBase &par) : parent(par)
6748         { init(); }
6750     /**
6751      *
6752      */
6753     Task(const Task &other) : parent(other.parent)
6754         { init(); assign(other); }
6756     /**
6757      *
6758      */
6759     Task &operator=(const Task &other)
6760         { assign(other); return *this; }
6762     /**
6763      *
6764      */
6765     virtual ~Task()
6766         { }
6769     /**
6770      *
6771      */
6772     virtual MakeBase &getParent()
6773         { return parent; }
6775      /**
6776      *
6777      */
6778     virtual int  getType()
6779         { return type; }
6781     /**
6782      *
6783      */
6784     virtual void setType(int val)
6785         { type = val; }
6787     /**
6788      *
6789      */
6790     virtual String getName()
6791         { return name; }
6793     /**
6794      *
6795      */
6796     virtual bool execute()
6797         { return true; }
6799     /**
6800      *
6801      */
6802     virtual bool parse(Element *elem)
6803         { return true; }
6805     /**
6806      *
6807      */
6808     Task *createTask(Element *elem, int lineNr);
6811 protected:
6813     void init()
6814         {
6815         type = TASK_NONE;
6816         name = "none";
6817         }
6819     void assign(const Task &other)
6820         {
6821         type = other.type;
6822         name = other.name;
6823         }
6824         
6825     /**
6826      *  Show task status
6827      */
6828     void taskstatus(const char *fmt, ...)
6829         {
6830         va_list args;
6831         va_start(args,fmt);
6832         fprintf(stdout, "    %s : ", name.c_str());
6833         vfprintf(stdout, fmt, args);
6834         fprintf(stdout, "\n");
6835         va_end(args) ;
6836         }
6838     String getAttribute(Element *elem, const String &attrName)
6839         {
6840         String str;
6841         return str;
6842         }
6844     MakeBase &parent;
6846     int type;
6848     String name;
6849 };
6853 /**
6854  * This task runs the C/C++ compiler.  The compiler is invoked
6855  * for all .c or .cpp files which are newer than their correcsponding
6856  * .o files.  
6857  */
6858 class TaskCC : public Task
6860 public:
6862     TaskCC(MakeBase &par) : Task(par)
6863         {
6864         type = TASK_CC;
6865         name = "cc";
6866         }
6868     virtual ~TaskCC()
6869         {}
6870         
6871     virtual bool isExcludedInc(const String &dirname)
6872         {
6873         for (unsigned int i=0 ; i<excludeInc.size() ; i++)
6874             {
6875             String fname = excludeInc[i];
6876             if (fname == dirname)
6877                 return true;
6878             }
6879         return false;
6880         }
6882     virtual bool execute()
6883         {
6884         //evaluate our parameters
6885         String command         = parent.eval(commandOpt, "gcc");
6886         String ccCommand       = parent.eval(ccCommandOpt, "gcc");
6887         String cxxCommand      = parent.eval(cxxCommandOpt, "g++");
6888         String source          = parent.eval(sourceOpt, ".");
6889         String dest            = parent.eval(destOpt, ".");
6890         String flags           = parent.eval(flagsOpt, "");
6891         String defines         = parent.eval(definesOpt, "");
6892         String includes        = parent.eval(includesOpt, "");
6893         bool continueOnError   = parent.evalBool(continueOnErrorOpt, true);
6894         bool refreshCache      = parent.evalBool(refreshCacheOpt, false);
6896         if (!listFiles(parent, fileSet))
6897             return false;
6898             
6899         FILE *f = NULL;
6900         f = fopen("compile.lst", "w");
6902         //refreshCache is probably false here, unless specified otherwise
6903         String fullName = parent.resolve("build.dep");
6904         if (refreshCache || isNewerThan(parent.getURI().getPath(), fullName))
6905             {
6906             taskstatus("regenerating C/C++ dependency cache");
6907             refreshCache = true;
6908             }
6910         DepTool depTool;
6911         depTool.setSourceDirectory(source);
6912         depTool.setFileList(fileSet.getFiles());
6913         std::vector<DepRec> deps =
6914              depTool.getDepFile("build.dep", refreshCache);
6915         
6916         String incs;
6917         incs.append("-I");
6918         incs.append(parent.resolve("."));
6919         incs.append(" ");
6920         if (includes.size()>0)
6921             {
6922             incs.append(includes);
6923             incs.append(" ");
6924             }
6925         std::set<String> paths;
6926         std::vector<DepRec>::iterator viter;
6927         for (viter=deps.begin() ; viter!=deps.end() ; viter++)
6928             {
6929             DepRec dep = *viter;
6930             if (dep.path.size()>0)
6931                 paths.insert(dep.path);
6932             }
6933         if (source.size()>0)
6934             {
6935             incs.append(" -I");
6936             incs.append(parent.resolve(source));
6937             incs.append(" ");
6938             }
6939         std::set<String>::iterator setIter;
6940         for (setIter=paths.begin() ; setIter!=paths.end() ; setIter++)
6941             {
6942             String dirName = *setIter;
6943             //check excludeInc to see if we dont want to include this dir
6944             if (isExcludedInc(dirName))
6945                 continue;
6946             incs.append(" -I");
6947             String dname;
6948             if (source.size()>0)
6949                 {
6950                 dname.append(source);
6951                 dname.append("/");
6952                 }
6953             dname.append(dirName);
6954             incs.append(parent.resolve(dname));
6955             }
6956             
6957         /**
6958          * Compile each of the C files that need it
6959          */
6960         bool errorOccurred = false;                 
6961         std::vector<String> cfiles;
6962         for (viter=deps.begin() ; viter!=deps.end() ; viter++)
6963             {
6964             DepRec dep = *viter;
6966             //## Select command
6967             String sfx = dep.suffix;
6968             String command = ccCommand;
6969             if (sfx == "cpp" || sfx == "cxx" || sfx == "c++" ||
6970                  sfx == "cc" || sfx == "CC")
6971                 command = cxxCommand;
6972  
6973             //## Make paths
6974             String destPath = dest;
6975             String srcPath  = source;
6976             if (dep.path.size()>0)
6977                 {
6978                 destPath.append("/");
6979                 destPath.append(dep.path);
6980                 srcPath.append("/");
6981                 srcPath.append(dep.path);
6982                 }
6983             //## Make sure destination directory exists
6984             if (!createDirectory(destPath))
6985                 return false;
6986                 
6987             //## Check whether it needs to be done
6988             String destName;
6989             if (destPath.size()>0)
6990                 {
6991                 destName.append(destPath);
6992                 destName.append("/");
6993                 }
6994             destName.append(dep.name);
6995             destName.append(".o");
6996             String destFullName = parent.resolve(destName);
6997             String srcName;
6998             if (srcPath.size()>0)
6999                 {
7000                 srcName.append(srcPath);
7001                 srcName.append("/");
7002                 }
7003             srcName.append(dep.name);
7004             srcName.append(".");
7005             srcName.append(dep.suffix);
7006             String srcFullName = parent.resolve(srcName);
7007             bool compileMe = false;
7008             //# First we check if the source is newer than the .o
7009             if (isNewerThan(srcFullName, destFullName))
7010                 {
7011                 taskstatus("compile of %s required by source: %s",
7012                         destFullName.c_str(), srcFullName.c_str());
7013                 compileMe = true;
7014                 }
7015             else
7016                 {
7017                 //# secondly, we check if any of the included dependencies
7018                 //# of the .c/.cpp is newer than the .o
7019                 for (unsigned int i=0 ; i<dep.files.size() ; i++)
7020                     {
7021                     String depName;
7022                     if (source.size()>0)
7023                         {
7024                         depName.append(source);
7025                         depName.append("/");
7026                         }
7027                     depName.append(dep.files[i]);
7028                     String depFullName = parent.resolve(depName);
7029                     bool depRequires = isNewerThan(depFullName, destFullName);
7030                     //trace("%d %s %s\n", depRequires,
7031                     //        destFullName.c_str(), depFullName.c_str());
7032                     if (depRequires)
7033                         {
7034                         taskstatus("compile of %s required by included: %s",
7035                                 destFullName.c_str(), depFullName.c_str());
7036                         compileMe = true;
7037                         break;
7038                         }
7039                     }
7040                 }
7041             if (!compileMe)
7042                 {
7043                 continue;
7044                 }
7046             //## Assemble the command
7047             String cmd = command;
7048             cmd.append(" -c ");
7049             cmd.append(flags);
7050             cmd.append(" ");
7051             cmd.append(defines);
7052             cmd.append(" ");
7053             cmd.append(incs);
7054             cmd.append(" ");
7055             cmd.append(srcFullName);
7056             cmd.append(" -o ");
7057             cmd.append(destFullName);
7059             //## Execute the command
7061             String outString, errString;
7062             bool ret = executeCommand(cmd.c_str(), "", outString, errString);
7064             if (f)
7065                 {
7066                 fprintf(f, "########################### File : %s\n",
7067                              srcFullName.c_str());
7068                 fprintf(f, "#### COMMAND ###\n");
7069                 int col = 0;
7070                 for (unsigned int i = 0 ; i < cmd.size() ; i++)
7071                     {
7072                     char ch = cmd[i];
7073                     if (isspace(ch)  && col > 63)
7074                         {
7075                         fputc('\n', f);
7076                         col = 0;
7077                         }
7078                     else
7079                         {
7080                         fputc(ch, f);
7081                         col++;
7082                         }
7083                     if (col > 76)
7084                         {
7085                         fputc('\n', f);
7086                         col = 0;
7087                         }
7088                     }
7089                 fprintf(f, "\n");
7090                 fprintf(f, "#### STDOUT ###\n%s\n", outString.c_str());
7091                 fprintf(f, "#### STDERR ###\n%s\n\n", errString.c_str());
7092                 fflush(f);
7093                 }
7094             if (!ret)
7095                 {
7096                 error("problem compiling: %s", errString.c_str());
7097                 errorOccurred = true;
7098                 }
7099             if (errorOccurred && !continueOnError)
7100                 break;
7102             removeFromStatCache(getNativePath(destFullName));
7103             }
7105         if (f)
7106             {
7107             fclose(f);
7108             }
7109         
7110         return !errorOccurred;
7111         }
7114     virtual bool parse(Element *elem)
7115         {
7116         String s;
7117         if (!parent.getAttribute(elem, "command", commandOpt))
7118             return false;
7119         if (commandOpt.size()>0)
7120             { cxxCommandOpt = ccCommandOpt = commandOpt; }
7121         if (!parent.getAttribute(elem, "cc", ccCommandOpt))
7122             return false;
7123         if (!parent.getAttribute(elem, "cxx", cxxCommandOpt))
7124             return false;
7125         if (!parent.getAttribute(elem, "destdir", destOpt))
7126             return false;
7127         if (!parent.getAttribute(elem, "continueOnError", continueOnErrorOpt))
7128             return false;
7129         if (!parent.getAttribute(elem, "refreshCache", refreshCacheOpt))
7130             return false;
7132         std::vector<Element *> children = elem->getChildren();
7133         for (unsigned int i=0 ; i<children.size() ; i++)
7134             {
7135             Element *child = children[i];
7136             String tagName = child->getName();
7137             if (tagName == "flags")
7138                 {
7139                 if (!parent.getValue(child, flagsOpt))
7140                     return false;
7141                 flagsOpt = strip(flagsOpt);
7142                 }
7143             else if (tagName == "includes")
7144                 {
7145                 if (!parent.getValue(child, includesOpt))
7146                     return false;
7147                 includesOpt = strip(includesOpt);
7148                 }
7149             else if (tagName == "defines")
7150                 {
7151                 if (!parent.getValue(child, definesOpt))
7152                     return false;
7153                 definesOpt = strip(definesOpt);
7154                 }
7155             else if (tagName == "fileset")
7156                 {
7157                 if (!parseFileSet(child, parent, fileSet))
7158                     return false;
7159                 sourceOpt = fileSet.getDirectory();
7160                 }
7161             else if (tagName == "excludeinc")
7162                 {
7163                 if (!parseFileList(child, parent, excludeInc))
7164                     return false;
7165                 }
7166             }
7168         return true;
7169         }
7170         
7171 protected:
7173     String   commandOpt;
7174     String   ccCommandOpt;
7175     String   cxxCommandOpt;
7176     String   sourceOpt;
7177     String   destOpt;
7178     String   flagsOpt;
7179     String   definesOpt;
7180     String   includesOpt;
7181     String   continueOnErrorOpt;
7182     String   refreshCacheOpt;
7183     FileSet  fileSet;
7184     FileList excludeInc;
7185     
7186 };
7190 /**
7191  *
7192  */
7193 class TaskCopy : public Task
7195 public:
7197     typedef enum
7198         {
7199         CP_NONE,
7200         CP_TOFILE,
7201         CP_TODIR
7202         } CopyType;
7204     TaskCopy(MakeBase &par) : Task(par)
7205         {
7206         type        = TASK_COPY;
7207         name        = "copy";
7208         cptype      = CP_NONE;
7209         haveFileSet = false;
7210         }
7212     virtual ~TaskCopy()
7213         {}
7215     virtual bool execute()
7216         {
7217         String fileName   = parent.eval(fileNameOpt   , ".");
7218         String toFileName = parent.eval(toFileNameOpt , ".");
7219         String toDirName  = parent.eval(toDirNameOpt  , ".");
7220         bool   verbose    = parent.evalBool(verboseOpt, false);
7221         switch (cptype)
7222            {
7223            case CP_TOFILE:
7224                {
7225                if (fileName.size()>0)
7226                    {
7227                    taskstatus("%s to %s",
7228                         fileName.c_str(), toFileName.c_str());
7229                    String fullSource = parent.resolve(fileName);
7230                    String fullDest = parent.resolve(toFileName);
7231                    if (verbose)
7232                        taskstatus("copy %s to file %s", fullSource.c_str(),
7233                                           fullDest.c_str());
7234                    if (!isRegularFile(fullSource))
7235                        {
7236                        error("copy : file %s does not exist", fullSource.c_str());
7237                        return false;
7238                        }
7239                    if (!isNewerThan(fullSource, fullDest))
7240                        {
7241                        taskstatus("skipped");
7242                        return true;
7243                        }
7244                    if (!copyFile(fullSource, fullDest))
7245                        return false;
7246                    taskstatus("1 file copied");
7247                    }
7248                return true;
7249                }
7250            case CP_TODIR:
7251                {
7252                if (haveFileSet)
7253                    {
7254                    if (!listFiles(parent, fileSet))
7255                        return false;
7256                    String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
7258                    taskstatus("%s to %s",
7259                        fileSetDir.c_str(), toDirName.c_str());
7261                    int nrFiles = 0;
7262                    for (unsigned int i=0 ; i<fileSet.size() ; i++)
7263                        {
7264                        String fileName = fileSet[i];
7266                        String sourcePath;
7267                        if (fileSetDir.size()>0)
7268                            {
7269                            sourcePath.append(fileSetDir);
7270                            sourcePath.append("/");
7271                            }
7272                        sourcePath.append(fileName);
7273                        String fullSource = parent.resolve(sourcePath);
7274                        
7275                        //Get the immediate parent directory's base name
7276                        String baseFileSetDir = fileSetDir;
7277                        unsigned int pos = baseFileSetDir.find_last_of('/');
7278                        if (pos!=baseFileSetDir.npos &&
7279                                   pos < baseFileSetDir.size()-1)
7280                            baseFileSetDir =
7281                               baseFileSetDir.substr(pos+1,
7282                                    baseFileSetDir.size());
7283                        //Now make the new path
7284                        String destPath;
7285                        if (toDirName.size()>0)
7286                            {
7287                            destPath.append(toDirName);
7288                            destPath.append("/");
7289                            }
7290                        if (baseFileSetDir.size()>0)
7291                            {
7292                            destPath.append(baseFileSetDir);
7293                            destPath.append("/");
7294                            }
7295                        destPath.append(fileName);
7296                        String fullDest = parent.resolve(destPath);
7297                        //trace("fileName:%s", fileName.c_str());
7298                        if (verbose)
7299                            taskstatus("copy %s to new dir : %s",
7300                                  fullSource.c_str(), fullDest.c_str());
7301                        if (!isNewerThan(fullSource, fullDest))
7302                            {
7303                            if (verbose)
7304                                taskstatus("copy skipping %s", fullSource.c_str());
7305                            continue;
7306                            }
7307                        if (!copyFile(fullSource, fullDest))
7308                            return false;
7309                        nrFiles++;
7310                        }
7311                    taskstatus("%d file(s) copied", nrFiles);
7312                    }
7313                else //file source
7314                    {
7315                    //For file->dir we want only the basename of
7316                    //the source appended to the dest dir
7317                    taskstatus("%s to %s", 
7318                        fileName.c_str(), toDirName.c_str());
7319                    String baseName = fileName;
7320                    unsigned int pos = baseName.find_last_of('/');
7321                    if (pos!=baseName.npos && pos<baseName.size()-1)
7322                        baseName = baseName.substr(pos+1, baseName.size());
7323                    String fullSource = parent.resolve(fileName);
7324                    String destPath;
7325                    if (toDirName.size()>0)
7326                        {
7327                        destPath.append(toDirName);
7328                        destPath.append("/");
7329                        }
7330                    destPath.append(baseName);
7331                    String fullDest = parent.resolve(destPath);
7332                    if (verbose)
7333                        taskstatus("file %s to new dir : %s", fullSource.c_str(),
7334                                           fullDest.c_str());
7335                    if (!isRegularFile(fullSource))
7336                        {
7337                        error("copy : file %s does not exist", fullSource.c_str());
7338                        return false;
7339                        }
7340                    if (!isNewerThan(fullSource, fullDest))
7341                        {
7342                        taskstatus("skipped");
7343                        return true;
7344                        }
7345                    if (!copyFile(fullSource, fullDest))
7346                        return false;
7347                    taskstatus("1 file copied");
7348                    }
7349                return true;
7350                }
7351            }
7352         return true;
7353         }
7356     virtual bool parse(Element *elem)
7357         {
7358         if (!parent.getAttribute(elem, "file", fileNameOpt))
7359             return false;
7360         if (!parent.getAttribute(elem, "tofile", toFileNameOpt))
7361             return false;
7362         if (toFileNameOpt.size() > 0)
7363             cptype = CP_TOFILE;
7364         if (!parent.getAttribute(elem, "todir", toDirNameOpt))
7365             return false;
7366         if (toDirNameOpt.size() > 0)
7367             cptype = CP_TODIR;
7368         if (!parent.getAttribute(elem, "verbose", verboseOpt))
7369             return false;
7370             
7371         haveFileSet = false;
7372         
7373         std::vector<Element *> children = elem->getChildren();
7374         for (unsigned int i=0 ; i<children.size() ; i++)
7375             {
7376             Element *child = children[i];
7377             String tagName = child->getName();
7378             if (tagName == "fileset")
7379                 {
7380                 if (!parseFileSet(child, parent, fileSet))
7381                     {
7382                     error("problem getting fileset");
7383                     return false;
7384                     }
7385                 haveFileSet = true;
7386                 }
7387             }
7389         //Perform validity checks
7390         if (fileNameOpt.size()>0 && fileSet.size()>0)
7391             {
7392             error("<copy> can only have one of : file= and <fileset>");
7393             return false;
7394             }
7395         if (toFileNameOpt.size()>0 && toDirNameOpt.size()>0)
7396             {
7397             error("<copy> can only have one of : tofile= or todir=");
7398             return false;
7399             }
7400         if (haveFileSet && toDirNameOpt.size()==0)
7401             {
7402             error("a <copy> task with a <fileset> must have : todir=");
7403             return false;
7404             }
7405         if (cptype == CP_TOFILE && fileNameOpt.size()==0)
7406             {
7407             error("<copy> tofile= must be associated with : file=");
7408             return false;
7409             }
7410         if (cptype == CP_TODIR && fileNameOpt.size()==0 && !haveFileSet)
7411             {
7412             error("<copy> todir= must be associated with : file= or <fileset>");
7413             return false;
7414             }
7416         return true;
7417         }
7418         
7419 private:
7421     int cptype;
7422     bool haveFileSet;
7424     FileSet fileSet;
7425     String  fileNameOpt;
7426     String  toFileNameOpt;
7427     String  toDirNameOpt;
7428     String  verboseOpt;
7429 };
7432 /**
7433  * Generate CxxTest files
7434  */
7435 class TaskCxxTestPart: public Task
7437 public:
7439     TaskCxxTestPart(MakeBase &par) : Task(par)
7440          {
7441          type    = TASK_CXXTEST_PART;
7442          name    = "cxxtestpart";
7443          }
7445     virtual ~TaskCxxTestPart()
7446         {}
7448     virtual bool execute()
7449         {
7450         if (!listFiles(parent, fileSet))
7451             return false;
7452         String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
7453                 
7454         String fullDest = parent.resolve(parent.eval(destPathOpt, "."));
7455         String cmd = parent.eval(commandOpt, "cxxtestgen.py");
7456         cmd.append(" --part -o ");
7457         cmd.append(fullDest);
7459         unsigned int newFiles = 0;
7460         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7461             {
7462             String fileName = fileSet[i];
7463             if (getSuffix(fileName) != "h")
7464                 continue;
7465             String sourcePath;
7466             if (fileSetDir.size()>0)
7467                 {
7468                 sourcePath.append(fileSetDir);
7469                 sourcePath.append("/");
7470                 }
7471             sourcePath.append(fileName);
7472             String fullSource = parent.resolve(sourcePath);
7474             cmd.append(" ");
7475             cmd.append(fullSource);
7476             if (isNewerThan(fullSource, fullDest)) newFiles++;
7477             }
7478         
7479         if (newFiles>0) {
7480             size_t const lastSlash = fullDest.find_last_of('/');
7481             if (lastSlash != fullDest.npos) {
7482                 String directory(fullDest, 0, lastSlash);
7483                 if (!createDirectory(directory))
7484                     return false;
7485             }
7487             String outString, errString;
7488             if (!executeCommand(cmd.c_str(), "", outString, errString))
7489                 {
7490                 error("<cxxtestpart> problem: %s", errString.c_str());
7491                 return false;
7492                 }
7493             removeFromStatCache(getNativePath(fullDest));
7494         }
7496         return true;
7497         }
7499     virtual bool parse(Element *elem)
7500         {
7501         if (!parent.getAttribute(elem, "command", commandOpt))
7502             return false;
7503         if (!parent.getAttribute(elem, "out", destPathOpt))
7504             return false;
7505             
7506         std::vector<Element *> children = elem->getChildren();
7507         for (unsigned int i=0 ; i<children.size() ; i++)
7508             {
7509             Element *child = children[i];
7510             String tagName = child->getName();
7511             if (tagName == "fileset")
7512                 {
7513                 if (!parseFileSet(child, parent, fileSet))
7514                     return false;
7515                 }
7516             }
7517         return true;
7518         }
7520 private:
7522     String  commandOpt;
7523     String  destPathOpt;
7524     FileSet fileSet;
7526 };
7529 /**
7530  * Generate the CxxTest root file
7531  */
7532 class TaskCxxTestRoot: public Task
7534 public:
7536     TaskCxxTestRoot(MakeBase &par) : Task(par)
7537          {
7538          type    = TASK_CXXTEST_ROOT;
7539          name    = "cxxtestroot";
7540          }
7542     virtual ~TaskCxxTestRoot()
7543         {}
7545     virtual bool execute()
7546         {
7547         if (!listFiles(parent, fileSet))
7548             return false;
7549         String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
7550         unsigned int newFiles = 0;
7551                 
7552         String fullDest = parent.resolve(parent.eval(destPathOpt, "."));
7553         String cmd = parent.eval(commandOpt, "cxxtestgen.py");
7554         cmd.append(" --root -o ");
7555         cmd.append(fullDest);
7556         String templateFile = parent.eval(templateFileOpt, "");
7557         if (templateFile.size()>0) {
7558             String fullTemplate = parent.resolve(templateFile);
7559             cmd.append(" --template=");
7560             cmd.append(fullTemplate);
7561             if (isNewerThan(fullTemplate, fullDest)) newFiles++;
7562         }
7564         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7565             {
7566             String fileName = fileSet[i];
7567             if (getSuffix(fileName) != "h")
7568                 continue;
7569             String sourcePath;
7570             if (fileSetDir.size()>0)
7571                 {
7572                 sourcePath.append(fileSetDir);
7573                 sourcePath.append("/");
7574                 }
7575             sourcePath.append(fileName);
7576             String fullSource = parent.resolve(sourcePath);
7578             cmd.append(" ");
7579             cmd.append(fullSource);
7580             if (isNewerThan(fullSource, fullDest)) newFiles++;
7581             }
7582         
7583         if (newFiles>0) {
7584             size_t const lastSlash = fullDest.find_last_of('/');
7585             if (lastSlash != fullDest.npos) {
7586                 String directory(fullDest, 0, lastSlash);
7587                 if (!createDirectory(directory))
7588                     return false;
7589             }
7591             String outString, errString;
7592             if (!executeCommand(cmd.c_str(), "", outString, errString))
7593                 {
7594                 error("<cxxtestroot> problem: %s", errString.c_str());
7595                 return false;
7596                 }
7597             removeFromStatCache(getNativePath(fullDest));
7598         }
7600         return true;
7601         }
7603     virtual bool parse(Element *elem)
7604         {
7605         if (!parent.getAttribute(elem, "command", commandOpt))
7606             return false;
7607         if (!parent.getAttribute(elem, "template", templateFileOpt))
7608             return false;
7609         if (!parent.getAttribute(elem, "out", destPathOpt))
7610             return false;
7611             
7612         std::vector<Element *> children = elem->getChildren();
7613         for (unsigned int i=0 ; i<children.size() ; i++)
7614             {
7615             Element *child = children[i];
7616             String tagName = child->getName();
7617             if (tagName == "fileset")
7618                 {
7619                 if (!parseFileSet(child, parent, fileSet))
7620                     return false;
7621                 }
7622             }
7623         return true;
7624         }
7626 private:
7628     String  commandOpt;
7629     String  templateFileOpt;
7630     String  destPathOpt;
7631     FileSet fileSet;
7633 };
7636 /**
7637  * Execute the CxxTest test executable
7638  */
7639 class TaskCxxTestRun: public Task
7641 public:
7643     TaskCxxTestRun(MakeBase &par) : Task(par)
7644          {
7645          type    = TASK_CXXTEST_RUN;
7646          name    = "cxxtestrun";
7647          }
7649     virtual ~TaskCxxTestRun()
7650         {}
7652     virtual bool execute()
7653         {
7654         unsigned int newFiles = 0;
7655                 
7656         String workingDir = parent.resolve(parent.eval(workingDirOpt, "inkscape"));
7657         String rawCmd = parent.eval(commandOpt, "build/cxxtests");
7659         String cmdExe;
7660         if (fileExists(rawCmd)) {
7661             cmdExe = rawCmd;
7662         } else if (fileExists(rawCmd + ".exe")) {
7663             cmdExe = rawCmd + ".exe";
7664         } else {
7665             error("<cxxtestrun> problem: cxxtests executable not found! (command=\"%s\")", rawCmd.c_str());
7666         }
7667         // Note that the log file names are based on the exact name used to call cxxtests (it uses argv[0] + ".log"/".xml")
7668         if (isNewerThan(cmdExe, rawCmd + ".log") || isNewerThan(cmdExe, rawCmd + ".xml")) newFiles++;
7670         // Prepend the necessary ../'s
7671         String cmd = rawCmd;
7672         unsigned int workingDirDepth = 0;
7673         bool wasSlash = true;
7674         for(size_t i=0; i<workingDir.size(); i++) {
7675             // This assumes no . and .. parts
7676             if (wasSlash && workingDir[i]!='/') workingDirDepth++;
7677             wasSlash = workingDir[i] == '/';
7678         }
7679         for(size_t i=0; i<workingDirDepth; i++) {
7680             cmd = "../" + cmd;
7681         }
7682         
7683         if (newFiles>0) {
7684             char olddir[1024];
7685             if (workingDir.size()>0) {
7686                 // TODO: Double-check usage of getcwd and handle chdir errors
7687                 getcwd(olddir, 1024);
7688                 chdir(workingDir.c_str());
7689             }
7691             String outString;
7692             if (!executeCommand(cmd.c_str(), "", outString, outString))
7693                 {
7694                 error("<cxxtestrun> problem: %s", outString.c_str());
7695                 return false;
7696                 }
7698             if (workingDir.size()>0) {
7699                 // TODO: Handle errors?
7700                 chdir(olddir);
7701             }
7703             removeFromStatCache(getNativePath(cmd + ".log"));
7704             removeFromStatCache(getNativePath(cmd + ".xml"));
7705         }
7707         return true;
7708         }
7710     virtual bool parse(Element *elem)
7711         {
7712         if (!parent.getAttribute(elem, "command", commandOpt))
7713             return false;
7714         if (!parent.getAttribute(elem, "workingdir", workingDirOpt))
7715             return false;
7716         return true;
7717         }
7719 private:
7721     String  commandOpt;
7722     String  workingDirOpt;
7724 };
7727 /**
7728  *
7729  */
7730 class TaskDelete : public Task
7732 public:
7734     typedef enum
7735         {
7736         DEL_FILE,
7737         DEL_DIR,
7738         DEL_FILESET
7739         } DeleteType;
7741     TaskDelete(MakeBase &par) : Task(par)
7742         { 
7743         type        = TASK_DELETE;
7744         name        = "delete";
7745         delType     = DEL_FILE;
7746         }
7748     virtual ~TaskDelete()
7749         {}
7751     virtual bool execute()
7752         {
7753         String dirName   = parent.eval(dirNameOpt, ".");
7754         String fileName  = parent.eval(fileNameOpt, ".");
7755         bool verbose     = parent.evalBool(verboseOpt, false);
7756         bool quiet       = parent.evalBool(quietOpt, false);
7757         bool failOnError = parent.evalBool(failOnErrorOpt, true);
7758         switch (delType)
7759             {
7760             case DEL_FILE:
7761                 {
7762                 taskstatus("file: %s", fileName.c_str());
7763                 String fullName = parent.resolve(fileName);
7764                 char *fname = (char *)fullName.c_str();
7765                 if (!quiet && verbose)
7766                     taskstatus("path: %s", fname);
7767                 if (failOnError && !removeFile(fullName))
7768                     {
7769                     //error("Could not delete file '%s'", fullName.c_str());
7770                     return false;
7771                     }
7772                 return true;
7773                 }
7774             case DEL_DIR:
7775                 {
7776                 taskstatus("dir: %s", dirName.c_str());
7777                 String fullDir = parent.resolve(dirName);
7778                 if (!quiet && verbose)
7779                     taskstatus("path: %s", fullDir.c_str());
7780                 if (failOnError && !removeDirectory(fullDir))
7781                     {
7782                     //error("Could not delete directory '%s'", fullDir.c_str());
7783                     return false;
7784                     }
7785                 return true;
7786                 }
7787             }
7788         return true;
7789         }
7791     virtual bool parse(Element *elem)
7792         {
7793         if (!parent.getAttribute(elem, "file", fileNameOpt))
7794             return false;
7795         if (fileNameOpt.size() > 0)
7796             delType = DEL_FILE;
7797         if (!parent.getAttribute(elem, "dir", dirNameOpt))
7798             return false;
7799         if (dirNameOpt.size() > 0)
7800             delType = DEL_DIR;
7801         if (fileNameOpt.size()>0 && dirNameOpt.size()>0)
7802             {
7803             error("<delete> can have one attribute of file= or dir=");
7804             return false;
7805             }
7806         if (fileNameOpt.size()==0 && dirNameOpt.size()==0)
7807             {
7808             error("<delete> must have one attribute of file= or dir=");
7809             return false;
7810             }
7811         if (!parent.getAttribute(elem, "verbose", verboseOpt))
7812             return false;
7813         if (!parent.getAttribute(elem, "quiet", quietOpt))
7814             return false;
7815         if (!parent.getAttribute(elem, "failonerror", failOnErrorOpt))
7816             return false;
7817         return true;
7818         }
7820 private:
7822     int delType;
7823     String dirNameOpt;
7824     String fileNameOpt;
7825     String verboseOpt;
7826     String quietOpt;
7827     String failOnErrorOpt;
7828 };
7831 /**
7832  * Send a message to stdout
7833  */
7834 class TaskEcho : public Task
7836 public:
7838     TaskEcho(MakeBase &par) : Task(par)
7839         { type = TASK_ECHO; name = "echo"; }
7841     virtual ~TaskEcho()
7842         {}
7844     virtual bool execute()
7845         {
7846         //let message have priority over text
7847         String message = parent.eval(messageOpt, "");
7848         String text    = parent.eval(textOpt, "");
7849         if (message.size() > 0)
7850             {
7851             fprintf(stdout, "%s\n", message.c_str());
7852             }
7853         else if (text.size() > 0)
7854             {
7855             fprintf(stdout, "%s\n", text.c_str());
7856             }
7857         return true;
7858         }
7860     virtual bool parse(Element *elem)
7861         {
7862         if (!parent.getValue(elem, textOpt))
7863             return false;
7864         textOpt    = leftJustify(textOpt);
7865         if (!parent.getAttribute(elem, "message", messageOpt))
7866             return false;
7867         return true;
7868         }
7870 private:
7872     String messageOpt;
7873     String textOpt;
7874 };
7878 /**
7879  *
7880  */
7881 class TaskJar : public Task
7883 public:
7885     TaskJar(MakeBase &par) : Task(par)
7886         { type = TASK_JAR; name = "jar"; }
7888     virtual ~TaskJar()
7889         {}
7891     virtual bool execute()
7892         {
7893         String command  = parent.eval(commandOpt, "jar");
7894         String basedir  = parent.eval(basedirOpt, ".");
7895         String destfile = parent.eval(destfileOpt, ".");
7897         String cmd = command;
7898         cmd.append(" -cf ");
7899         cmd.append(destfile);
7900         cmd.append(" -C ");
7901         cmd.append(basedir);
7902         cmd.append(" .");
7904         String execCmd = cmd;
7906         String outString, errString;
7907         bool ret = executeCommand(execCmd.c_str(), "", outString, errString);
7908         if (!ret)
7909             {
7910             error("<jar> command '%s' failed :\n %s",
7911                                       execCmd.c_str(), errString.c_str());
7912             return false;
7913             }
7914         removeFromStatCache(getNativePath(destfile));
7915         return true;
7916         }
7918     virtual bool parse(Element *elem)
7919         {
7920         if (!parent.getAttribute(elem, "command", commandOpt))
7921             return false;
7922         if (!parent.getAttribute(elem, "basedir", basedirOpt))
7923             return false;
7924         if (!parent.getAttribute(elem, "destfile", destfileOpt))
7925             return false;
7926         if (basedirOpt.size() == 0 || destfileOpt.size() == 0)
7927             {
7928             error("<jar> required both basedir and destfile attributes to be set");
7929             return false;
7930             }
7931         return true;
7932         }
7934 private:
7936     String commandOpt;
7937     String basedirOpt;
7938     String destfileOpt;
7939 };
7942 /**
7943  *
7944  */
7945 class TaskJavac : public Task
7947 public:
7949     TaskJavac(MakeBase &par) : Task(par)
7950         { 
7951         type = TASK_JAVAC; name = "javac";
7952         }
7954     virtual ~TaskJavac()
7955         {}
7957     virtual bool execute()
7958         {
7959         String command  = parent.eval(commandOpt, "javac");
7960         String srcdir   = parent.eval(srcdirOpt, ".");
7961         String destdir  = parent.eval(destdirOpt, ".");
7962         String target   = parent.eval(targetOpt, "");
7964         std::vector<String> fileList;
7965         if (!listFiles(srcdir, "", fileList))
7966             {
7967             return false;
7968             }
7969         String cmd = command;
7970         cmd.append(" -d ");
7971         cmd.append(destdir);
7972         cmd.append(" -classpath ");
7973         cmd.append(destdir);
7974         cmd.append(" -sourcepath ");
7975         cmd.append(srcdir);
7976         cmd.append(" ");
7977         if (target.size()>0)
7978             {
7979             cmd.append(" -target ");
7980             cmd.append(target);
7981             cmd.append(" ");
7982             }
7983         String fname = "javalist.btool";
7984         FILE *f = fopen(fname.c_str(), "w");
7985         int count = 0;
7986         for (unsigned int i=0 ; i<fileList.size() ; i++)
7987             {
7988             String fname = fileList[i];
7989             String srcName = fname;
7990             if (fname.size()<6) //x.java
7991                 continue;
7992             if (fname.compare(fname.size()-5, 5, ".java") != 0)
7993                 continue;
7994             String baseName = fname.substr(0, fname.size()-5);
7995             String destName = baseName;
7996             destName.append(".class");
7998             String fullSrc = srcdir;
7999             fullSrc.append("/");
8000             fullSrc.append(fname);
8001             String fullDest = destdir;
8002             fullDest.append("/");
8003             fullDest.append(destName);
8004             //trace("fullsrc:%s fulldest:%s", fullSrc.c_str(), fullDest.c_str());
8005             if (!isNewerThan(fullSrc, fullDest))
8006                 continue;
8008             count++;
8009             fprintf(f, "%s\n", fullSrc.c_str());
8010             }
8011         fclose(f);
8012         if (!count)
8013             {
8014             taskstatus("nothing to do");
8015             return true;
8016             }
8018         taskstatus("compiling %d files", count);
8020         String execCmd = cmd;
8021         execCmd.append("@");
8022         execCmd.append(fname);
8024         String outString, errString;
8025         bool ret = executeCommand(execCmd.c_str(), "", outString, errString);
8026         if (!ret)
8027             {
8028             error("<javac> command '%s' failed :\n %s",
8029                                       execCmd.c_str(), errString.c_str());
8030             return false;
8031             }
8032         // TODO: 
8033         //removeFromStatCache(getNativePath(........));
8034         return true;
8035         }
8037     virtual bool parse(Element *elem)
8038         {
8039         if (!parent.getAttribute(elem, "command", commandOpt))
8040             return false;
8041         if (!parent.getAttribute(elem, "srcdir", srcdirOpt))
8042             return false;
8043         if (!parent.getAttribute(elem, "destdir", destdirOpt))
8044             return false;
8045         if (srcdirOpt.size() == 0 || destdirOpt.size() == 0)
8046             {
8047             error("<javac> required both srcdir and destdir attributes to be set");
8048             return false;
8049             }
8050         if (!parent.getAttribute(elem, "target", targetOpt))
8051             return false;
8052         return true;
8053         }
8055 private:
8057     String commandOpt;
8058     String srcdirOpt;
8059     String destdirOpt;
8060     String targetOpt;
8062 };
8065 /**
8066  *
8067  */
8068 class TaskLink : public Task
8070 public:
8072     TaskLink(MakeBase &par) : Task(par)
8073         {
8074         type = TASK_LINK; name = "link";
8075         }
8077     virtual ~TaskLink()
8078         {}
8080     virtual bool execute()
8081         {
8082         String  command        = parent.eval(commandOpt, "g++");
8083         String  fileName       = parent.eval(fileNameOpt, "");
8084         String  flags          = parent.eval(flagsOpt, "");
8085         String  libs           = parent.eval(libsOpt, "");
8086         bool    doStrip        = parent.evalBool(doStripOpt, false);
8087         String  symFileName    = parent.eval(symFileNameOpt, "");
8088         String  stripCommand   = parent.eval(stripCommandOpt, "strip");
8089         String  objcopyCommand = parent.eval(objcopyCommandOpt, "objcopy");
8091         if (!listFiles(parent, fileSet))
8092             return false;
8093         String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
8094         //trace("%d files in %s", fileSet.size(), fileSetDir.c_str());
8095         bool doit = false;
8096         String fullTarget = parent.resolve(fileName);
8097         String cmd = command;
8098         cmd.append(" -o ");
8099         cmd.append(fullTarget);
8100         cmd.append(" ");
8101         cmd.append(flags);
8102         for (unsigned int i=0 ; i<fileSet.size() ; i++)
8103             {
8104             cmd.append(" ");
8105             String obj;
8106             if (fileSetDir.size()>0)
8107                 {
8108                 obj.append(fileSetDir);
8109                 obj.append("/");
8110                 }
8111             obj.append(fileSet[i]);
8112             String fullObj = parent.resolve(obj);
8113             String nativeFullObj = getNativePath(fullObj);
8114             cmd.append(nativeFullObj);
8115             //trace("link: tgt:%s obj:%s", fullTarget.c_str(),
8116             //          fullObj.c_str());
8117             if (isNewerThan(fullObj, fullTarget))
8118                 doit = true;
8119             }
8120         cmd.append(" ");
8121         cmd.append(libs);
8122         if (!doit)
8123             {
8124             //trace("link not needed");
8125             return true;
8126             }
8127         //trace("LINK cmd:%s", cmd.c_str());
8130         String outbuf, errbuf;
8131         if (!executeCommand(cmd.c_str(), "", outbuf, errbuf))
8132             {
8133             error("LINK problem: %s", errbuf.c_str());
8134             return false;
8135             }
8136         removeFromStatCache(getNativePath(fullTarget));
8138         if (symFileName.size()>0)
8139             {
8140             String symFullName = parent.resolve(symFileName);
8141             cmd = objcopyCommand;
8142             cmd.append(" --only-keep-debug ");
8143             cmd.append(getNativePath(fullTarget));
8144             cmd.append(" ");
8145             cmd.append(getNativePath(symFullName));
8146             if (!executeCommand(cmd, "", outbuf, errbuf))
8147                 {
8148                 error("<strip> symbol file failed : %s", errbuf.c_str());
8149                 return false;
8150                 }
8151             removeFromStatCache(getNativePath(symFullName));
8152             }
8153             
8154         if (doStrip)
8155             {
8156             cmd = stripCommand;
8157             cmd.append(" ");
8158             cmd.append(getNativePath(fullTarget));
8159             if (!executeCommand(cmd, "", outbuf, errbuf))
8160                {
8161                error("<strip> failed : %s", errbuf.c_str());
8162                return false;
8163                }
8164             removeFromStatCache(getNativePath(fullTarget));
8165             }
8167         return true;
8168         }
8170     virtual bool parse(Element *elem)
8171         {
8172         if (!parent.getAttribute(elem, "command", commandOpt))
8173             return false;
8174         if (!parent.getAttribute(elem, "objcopycommand", objcopyCommandOpt))
8175             return false;
8176         if (!parent.getAttribute(elem, "stripcommand", stripCommandOpt))
8177             return false;
8178         if (!parent.getAttribute(elem, "out", fileNameOpt))
8179             return false;
8180         if (!parent.getAttribute(elem, "strip", doStripOpt))
8181             return false;
8182         if (!parent.getAttribute(elem, "symfile", symFileNameOpt))
8183             return false;
8184             
8185         std::vector<Element *> children = elem->getChildren();
8186         for (unsigned int i=0 ; i<children.size() ; i++)
8187             {
8188             Element *child = children[i];
8189             String tagName = child->getName();
8190             if (tagName == "fileset")
8191                 {
8192                 if (!parseFileSet(child, parent, fileSet))
8193                     return false;
8194                 }
8195             else if (tagName == "flags")
8196                 {
8197                 if (!parent.getValue(child, flagsOpt))
8198                     return false;
8199                 flagsOpt = strip(flagsOpt);
8200                 }
8201             else if (tagName == "libs")
8202                 {
8203                 if (!parent.getValue(child, libsOpt))
8204                     return false;
8205                 libsOpt = strip(libsOpt);
8206                 }
8207             }
8208         return true;
8209         }
8211 private:
8213     FileSet fileSet;
8215     String  commandOpt;
8216     String  fileNameOpt;
8217     String  flagsOpt;
8218     String  libsOpt;
8219     String  doStripOpt;
8220     String  symFileNameOpt;
8221     String  stripCommandOpt;
8222     String  objcopyCommandOpt;
8224 };
8228 /**
8229  * Create a named file
8230  */
8231 class TaskMakeFile : public Task
8233 public:
8235     TaskMakeFile(MakeBase &par) : Task(par)
8236         { type = TASK_MAKEFILE; name = "makefile"; }
8238     virtual ~TaskMakeFile()
8239         {}
8241     virtual bool execute()
8242         {
8243         String fileName = parent.eval(fileNameOpt, "");
8244         bool force      = parent.evalBool(forceOpt, false);
8245         String text     = parent.eval(textOpt, "");
8247         taskstatus("%s", fileName.c_str());
8248         String fullName = parent.resolve(fileName);
8249         if (!force && !isNewerThan(parent.getURI().getPath(), fullName))
8250             {
8251             taskstatus("skipped");
8252             return true;
8253             }
8254         String fullNative = getNativePath(fullName);
8255         //trace("fullName:%s", fullName.c_str());
8256         FILE *f = fopen(fullNative.c_str(), "w");
8257         if (!f)
8258             {
8259             error("<makefile> could not open %s for writing : %s",
8260                 fullName.c_str(), strerror(errno));
8261             return false;
8262             }
8263         for (unsigned int i=0 ; i<text.size() ; i++)
8264             fputc(text[i], f);
8265         fputc('\n', f);
8266         fclose(f);
8267         removeFromStatCache(fullNative);
8268         return true;
8269         }
8271     virtual bool parse(Element *elem)
8272         {
8273         if (!parent.getAttribute(elem, "file", fileNameOpt))
8274             return false;
8275         if (!parent.getAttribute(elem, "force", forceOpt))
8276             return false;
8277         if (fileNameOpt.size() == 0)
8278             {
8279             error("<makefile> requires 'file=\"filename\"' attribute");
8280             return false;
8281             }
8282         if (!parent.getValue(elem, textOpt))
8283             return false;
8284         textOpt = leftJustify(textOpt);
8285         //trace("dirname:%s", dirName.c_str());
8286         return true;
8287         }
8289 private:
8291     String fileNameOpt;
8292     String forceOpt;
8293     String textOpt;
8294 };
8298 /**
8299  * Create a named directory
8300  */
8301 class TaskMkDir : public Task
8303 public:
8305     TaskMkDir(MakeBase &par) : Task(par)
8306         { type = TASK_MKDIR; name = "mkdir"; }
8308     virtual ~TaskMkDir()
8309         {}
8311     virtual bool execute()
8312         {
8313         String dirName = parent.eval(dirNameOpt, ".");
8314         
8315         taskstatus("%s", dirName.c_str());
8316         String fullDir = parent.resolve(dirName);
8317         //trace("fullDir:%s", fullDir.c_str());
8318         if (!createDirectory(fullDir))
8319             return false;
8320         return true;
8321         }
8323     virtual bool parse(Element *elem)
8324         {
8325         if (!parent.getAttribute(elem, "dir", dirNameOpt))
8326             return false;
8327         if (dirNameOpt.size() == 0)
8328             {
8329             error("<mkdir> requires 'dir=\"dirname\"' attribute");
8330             return false;
8331             }
8332         return true;
8333         }
8335 private:
8337     String dirNameOpt;
8338 };
8342 /**
8343  * Create a named directory
8344  */
8345 class TaskMsgFmt: public Task
8347 public:
8349     TaskMsgFmt(MakeBase &par) : Task(par)
8350          { type = TASK_MSGFMT;  name = "msgfmt"; }
8352     virtual ~TaskMsgFmt()
8353         {}
8355     virtual bool execute()
8356         {
8357         String  command   = parent.eval(commandOpt, "msgfmt");
8358         String  toDirName = parent.eval(toDirNameOpt, ".");
8359         String  outName   = parent.eval(outNameOpt, "");
8360         bool    owndir    = parent.evalBool(owndirOpt, false);
8362         if (!listFiles(parent, fileSet))
8363             return false;
8364         String fileSetDir = fileSet.getDirectory();
8366         //trace("msgfmt: %d", fileSet.size());
8367         for (unsigned int i=0 ; i<fileSet.size() ; i++)
8368             {
8369             String fileName = fileSet[i];
8370             if (getSuffix(fileName) != "po")
8371                 continue;
8372             String sourcePath;
8373             if (fileSetDir.size()>0)
8374                 {
8375                 sourcePath.append(fileSetDir);
8376                 sourcePath.append("/");
8377                 }
8378             sourcePath.append(fileName);
8379             String fullSource = parent.resolve(sourcePath);
8381             String destPath;
8382             if (toDirName.size()>0)
8383                 {
8384                 destPath.append(toDirName);
8385                 destPath.append("/");
8386                 }
8387             if (owndir)
8388                 {
8389                 String subdir = fileName;
8390                 unsigned int pos = subdir.find_last_of('.');
8391                 if (pos != subdir.npos)
8392                     subdir = subdir.substr(0, pos);
8393                 destPath.append(subdir);
8394                 destPath.append("/");
8395                 }
8396             //Pick the output file name
8397             if (outName.size() > 0)
8398                 {
8399                 destPath.append(outName);
8400                 }
8401             else
8402                 {
8403                 destPath.append(fileName);
8404                 destPath[destPath.size()-2] = 'm';
8405                 }
8407             String fullDest = parent.resolve(destPath);
8409             if (!isNewerThan(fullSource, fullDest))
8410                 {
8411                 //trace("skip %s", fullSource.c_str());
8412                 continue;
8413                 }
8414                 
8415             String cmd = command;
8416             cmd.append(" ");
8417             cmd.append(fullSource);
8418             cmd.append(" -o ");
8419             cmd.append(fullDest);
8420             
8421             int pos = fullDest.find_last_of('/');
8422             if (pos>0)
8423                 {
8424                 String fullDestPath = fullDest.substr(0, pos);
8425                 if (!createDirectory(fullDestPath))
8426                     return false;
8427                 }
8431             String outString, errString;
8432             if (!executeCommand(cmd.c_str(), "", outString, errString))
8433                 {
8434                 error("<msgfmt> problem: %s", errString.c_str());
8435                 return false;
8436                 }
8437             removeFromStatCache(getNativePath(fullDest));
8438             }
8440         return true;
8441         }
8443     virtual bool parse(Element *elem)
8444         {
8445         if (!parent.getAttribute(elem, "command", commandOpt))
8446             return false;
8447         if (!parent.getAttribute(elem, "todir", toDirNameOpt))
8448             return false;
8449         if (!parent.getAttribute(elem, "out", outNameOpt))
8450             return false;
8451         if (!parent.getAttribute(elem, "owndir", owndirOpt))
8452             return false;
8453             
8454         std::vector<Element *> children = elem->getChildren();
8455         for (unsigned int i=0 ; i<children.size() ; i++)
8456             {
8457             Element *child = children[i];
8458             String tagName = child->getName();
8459             if (tagName == "fileset")
8460                 {
8461                 if (!parseFileSet(child, parent, fileSet))
8462                     return false;
8463                 }
8464             }
8465         return true;
8466         }
8468 private:
8470     FileSet fileSet;
8472     String  commandOpt;
8473     String  toDirNameOpt;
8474     String  outNameOpt;
8475     String  owndirOpt;
8477 };
8481 /**
8482  *  Perform a Package-Config query similar to pkg-config
8483  */
8484 class TaskPkgConfig : public Task
8486 public:
8488     typedef enum
8489         {
8490         PKG_CONFIG_QUERY_CFLAGS,
8491         PKG_CONFIG_QUERY_LIBS,
8492         PKG_CONFIG_QUERY_ALL
8493         } QueryTypes;
8495     TaskPkgConfig(MakeBase &par) : Task(par)
8496         {
8497         type = TASK_PKG_CONFIG;
8498         name = "pkg-config";
8499         }
8501     virtual ~TaskPkgConfig()
8502         {}
8504     virtual bool execute()
8505         {
8506         String pkgName       = parent.eval(pkgNameOpt,      "");
8507         String prefix        = parent.eval(prefixOpt,       "");
8508         String propName      = parent.eval(propNameOpt,     "");
8509         String pkgConfigPath = parent.eval(pkgConfigPathOpt,"");
8510         String query         = parent.eval(queryOpt,        "all");
8512         String path = parent.resolve(pkgConfigPath);
8513         PkgConfig pkgconfig;
8514         pkgconfig.setPath(path);
8515         pkgconfig.setPrefix(prefix);
8516         if (!pkgconfig.query(pkgName))
8517             {
8518             error("<pkg-config> query failed for '%s", name.c_str());
8519             return false;
8520             }
8521             
8522         String val = "";
8523         if (query == "cflags")
8524             val = pkgconfig.getCflags();
8525         else if (query == "libs")
8526             val =pkgconfig.getLibs();
8527         else if (query == "all")
8528             val = pkgconfig.getAll();
8529         else
8530             {
8531             error("<pkg-config> unhandled query : %s", query.c_str());
8532             return false;
8533             }
8534         taskstatus("property %s = '%s'", propName.c_str(), val.c_str());
8535         parent.setProperty(propName, val);
8536         return true;
8537         }
8539     virtual bool parse(Element *elem)
8540         {
8541         //# NAME
8542         if (!parent.getAttribute(elem, "name", pkgNameOpt))
8543             return false;
8544         if (pkgNameOpt.size()==0)
8545             {
8546             error("<pkg-config> requires 'name=\"package\"' attribute");
8547             return false;
8548             }
8550         //# PROPERTY
8551         if (!parent.getAttribute(elem, "property", propNameOpt))
8552             return false;
8553         if (propNameOpt.size()==0)
8554             {
8555             error("<pkg-config> requires 'property=\"name\"' attribute");
8556             return false;
8557             }
8558         //# PATH
8559         if (!parent.getAttribute(elem, "path", pkgConfigPathOpt))
8560             return false;
8561         //# PREFIX
8562         if (!parent.getAttribute(elem, "prefix", prefixOpt))
8563             return false;
8564         //# QUERY
8565         if (!parent.getAttribute(elem, "query", queryOpt))
8566             return false;
8568         return true;
8569         }
8571 private:
8573     String queryOpt;
8574     String pkgNameOpt;
8575     String prefixOpt;
8576     String propNameOpt;
8577     String pkgConfigPathOpt;
8579 };
8586 /**
8587  *  Process an archive to allow random access
8588  */
8589 class TaskRanlib : public Task
8591 public:
8593     TaskRanlib(MakeBase &par) : Task(par)
8594         { type = TASK_RANLIB; name = "ranlib"; }
8596     virtual ~TaskRanlib()
8597         {}
8599     virtual bool execute()
8600         {
8601         String fileName = parent.eval(fileNameOpt, "");
8602         String command  = parent.eval(commandOpt, "ranlib");
8604         String fullName = parent.resolve(fileName);
8605         //trace("fullDir:%s", fullDir.c_str());
8606         String cmd = command;
8607         cmd.append(" ");
8608         cmd.append(fullName);
8609         String outbuf, errbuf;
8610         if (!executeCommand(cmd, "", outbuf, errbuf))
8611             return false;
8612         // TODO:
8613         //removeFromStatCache(getNativePath(fullDest));
8614         return true;
8615         }
8617     virtual bool parse(Element *elem)
8618         {
8619         if (!parent.getAttribute(elem, "command", commandOpt))
8620             return false;
8621         if (!parent.getAttribute(elem, "file", fileNameOpt))
8622             return false;
8623         if (fileNameOpt.size() == 0)
8624             {
8625             error("<ranlib> requires 'file=\"fileNname\"' attribute");
8626             return false;
8627             }
8628         return true;
8629         }
8631 private:
8633     String fileNameOpt;
8634     String commandOpt;
8635 };
8639 /**
8640  * Compile a resource file into a binary object
8641  */
8642 class TaskRC : public Task
8644 public:
8646     TaskRC(MakeBase &par) : Task(par)
8647         { type = TASK_RC; name = "rc"; }
8649     virtual ~TaskRC()
8650         {}
8652     virtual bool execute()
8653         {
8654         String command  = parent.eval(commandOpt,  "windres");
8655         String flags    = parent.eval(flagsOpt,    "");
8656         String fileName = parent.eval(fileNameOpt, "");
8657         String outName  = parent.eval(outNameOpt,  "");
8659         String fullFile = parent.resolve(fileName);
8660         String fullOut  = parent.resolve(outName);
8661         if (!isNewerThan(fullFile, fullOut))
8662             return true;
8663         String cmd = command;
8664         cmd.append(" -o ");
8665         cmd.append(fullOut);
8666         cmd.append(" ");
8667         cmd.append(flags);
8668         cmd.append(" ");
8669         cmd.append(fullFile);
8671         String outString, errString;
8672         if (!executeCommand(cmd.c_str(), "", outString, errString))
8673             {
8674             error("RC problem: %s", errString.c_str());
8675             return false;
8676             }
8677         removeFromStatCache(getNativePath(fullOut));
8678         return true;
8679         }
8681     virtual bool parse(Element *elem)
8682         {
8683         if (!parent.getAttribute(elem, "command", commandOpt))
8684             return false;
8685         if (!parent.getAttribute(elem, "file", fileNameOpt))
8686             return false;
8687         if (!parent.getAttribute(elem, "out", outNameOpt))
8688             return false;
8689         std::vector<Element *> children = elem->getChildren();
8690         for (unsigned int i=0 ; i<children.size() ; i++)
8691             {
8692             Element *child = children[i];
8693             String tagName = child->getName();
8694             if (tagName == "flags")
8695                 {
8696                 if (!parent.getValue(child, flagsOpt))
8697                     return false;
8698                 }
8699             }
8700         return true;
8701         }
8703 private:
8705     String commandOpt;
8706     String flagsOpt;
8707     String fileNameOpt;
8708     String outNameOpt;
8710 };
8714 /**
8715  *  Collect .o's into a .so or DLL
8716  */
8717 class TaskSharedLib : public Task
8719 public:
8721     TaskSharedLib(MakeBase &par) : Task(par)
8722         { type = TASK_SHAREDLIB; name = "dll"; }
8724     virtual ~TaskSharedLib()
8725         {}
8727     virtual bool execute()
8728         {
8729         String command     = parent.eval(commandOpt, "dllwrap");
8730         String fileName    = parent.eval(fileNameOpt, "");
8731         String defFileName = parent.eval(defFileNameOpt, "");
8732         String impFileName = parent.eval(impFileNameOpt, "");
8733         String libs        = parent.eval(libsOpt, "");
8735         //trace("###########HERE %d", fileSet.size());
8736         bool doit = false;
8737         
8738         String fullOut = parent.resolve(fileName);
8739         //trace("ar fullout: %s", fullOut.c_str());
8740         
8741         if (!listFiles(parent, fileSet))
8742             return false;
8743         String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
8745         for (unsigned int i=0 ; i<fileSet.size() ; i++)
8746             {
8747             String fname;
8748             if (fileSetDir.size()>0)
8749                 {
8750                 fname.append(fileSetDir);
8751                 fname.append("/");
8752                 }
8753             fname.append(fileSet[i]);
8754             String fullName = parent.resolve(fname);
8755             //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
8756             if (isNewerThan(fullName, fullOut))
8757                 doit = true;
8758             }
8759         //trace("Needs it:%d", doit);
8760         if (!doit)
8761             {
8762             return true;
8763             }
8765         String cmd = "dllwrap";
8766         cmd.append(" -o ");
8767         cmd.append(fullOut);
8768         if (defFileName.size()>0)
8769             {
8770             cmd.append(" --def ");
8771             cmd.append(defFileName);
8772             cmd.append(" ");
8773             }
8774         if (impFileName.size()>0)
8775             {
8776             cmd.append(" --implib ");
8777             cmd.append(impFileName);
8778             cmd.append(" ");
8779             }
8780         for (unsigned int i=0 ; i<fileSet.size() ; i++)
8781             {
8782             String fname;
8783             if (fileSetDir.size()>0)
8784                 {
8785                 fname.append(fileSetDir);
8786                 fname.append("/");
8787                 }
8788             fname.append(fileSet[i]);
8789             String fullName = parent.resolve(fname);
8791             cmd.append(" ");
8792             cmd.append(fullName);
8793             }
8794         cmd.append(" ");
8795         cmd.append(libs);
8797         String outString, errString;
8798         if (!executeCommand(cmd.c_str(), "", outString, errString))
8799             {
8800             error("<sharedlib> problem: %s", errString.c_str());
8801             return false;
8802             }
8803         removeFromStatCache(getNativePath(fullOut));
8804         return true;
8805         }
8807     virtual bool parse(Element *elem)
8808         {
8809         if (!parent.getAttribute(elem, "command", commandOpt))
8810             return false;
8811         if (!parent.getAttribute(elem, "file", fileNameOpt))
8812             return false;
8813         if (!parent.getAttribute(elem, "import", impFileNameOpt))
8814             return false;
8815         if (!parent.getAttribute(elem, "def", defFileNameOpt))
8816             return false;
8817             
8818         std::vector<Element *> children = elem->getChildren();
8819         for (unsigned int i=0 ; i<children.size() ; i++)
8820             {
8821             Element *child = children[i];
8822             String tagName = child->getName();
8823             if (tagName == "fileset")
8824                 {
8825                 if (!parseFileSet(child, parent, fileSet))
8826                     return false;
8827                 }
8828             else if (tagName == "libs")
8829                 {
8830                 if (!parent.getValue(child, libsOpt))
8831                     return false;
8832                 libsOpt = strip(libsOpt);
8833                 }
8834             }
8835         return true;
8836         }
8838 private:
8840     FileSet fileSet;
8842     String commandOpt;
8843     String fileNameOpt;
8844     String defFileNameOpt;
8845     String impFileNameOpt;
8846     String libsOpt;
8848 };
8852 /**
8853  * Run the "ar" command to archive .o's into a .a
8854  */
8855 class TaskStaticLib : public Task
8857 public:
8859     TaskStaticLib(MakeBase &par) : Task(par)
8860         { type = TASK_STATICLIB; name = "staticlib"; }
8862     virtual ~TaskStaticLib()
8863         {}
8865     virtual bool execute()
8866         {
8867         String command = parent.eval(commandOpt, "ar crv");
8868         String fileName = parent.eval(fileNameOpt, "");
8870         bool doit = false;
8871         
8872         String fullOut = parent.resolve(fileName);
8873         //trace("ar fullout: %s", fullOut.c_str());
8874         
8875         if (!listFiles(parent, fileSet))
8876             return false;
8877         String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
8878         //trace("###########HERE %s", fileSetDir.c_str());
8880         for (unsigned int i=0 ; i<fileSet.size() ; i++)
8881             {
8882             String fname;
8883             if (fileSetDir.size()>0)
8884                 {
8885                 fname.append(fileSetDir);
8886                 fname.append("/");
8887                 }
8888             fname.append(fileSet[i]);
8889             String fullName = parent.resolve(fname);
8890             //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
8891             if (isNewerThan(fullName, fullOut))
8892                 doit = true;
8893             }
8894         //trace("Needs it:%d", doit);
8895         if (!doit)
8896             {
8897             return true;
8898             }
8900         String cmd = command;
8901         cmd.append(" ");
8902         cmd.append(fullOut);
8903         for (unsigned int i=0 ; i<fileSet.size() ; i++)
8904             {
8905             String fname;
8906             if (fileSetDir.size()>0)
8907                 {
8908                 fname.append(fileSetDir);
8909                 fname.append("/");
8910                 }
8911             fname.append(fileSet[i]);
8912             String fullName = parent.resolve(fname);
8914             cmd.append(" ");
8915             cmd.append(fullName);
8916             }
8918         String outString, errString;
8919         if (!executeCommand(cmd.c_str(), "", outString, errString))
8920             {
8921             error("<staticlib> problem: %s", errString.c_str());
8922             return false;
8923             }
8924         removeFromStatCache(getNativePath(fullOut));
8925         return true;
8926         }
8929     virtual bool parse(Element *elem)
8930         {
8931         if (!parent.getAttribute(elem, "command", commandOpt))
8932             return false;
8933         if (!parent.getAttribute(elem, "file", fileNameOpt))
8934             return false;
8935             
8936         std::vector<Element *> children = elem->getChildren();
8937         for (unsigned int i=0 ; i<children.size() ; i++)
8938             {
8939             Element *child = children[i];
8940             String tagName = child->getName();
8941             if (tagName == "fileset")
8942                 {
8943                 if (!parseFileSet(child, parent, fileSet))
8944                     return false;
8945                 }
8946             }
8947         return true;
8948         }
8950 private:
8952     FileSet fileSet;
8954     String commandOpt;
8955     String fileNameOpt;
8957 };
8962 /**
8963  * Strip an executable
8964  */
8965 class TaskStrip : public Task
8967 public:
8969     TaskStrip(MakeBase &par) : Task(par)
8970         { type = TASK_STRIP; name = "strip"; }
8972     virtual ~TaskStrip()
8973         {}
8975     virtual bool execute()
8976         {
8977         String command     = parent.eval(commandOpt, "strip");
8978         String fileName    = parent.eval(fileNameOpt, "");
8979         String symFileName = parent.eval(symFileNameOpt, "");
8981         String fullName = parent.resolve(fileName);
8982         //trace("fullDir:%s", fullDir.c_str());
8983         String cmd;
8984         String outbuf, errbuf;
8986         if (symFileName.size()>0)
8987             {
8988             String symFullName = parent.resolve(symFileName);
8989             cmd = "objcopy --only-keep-debug ";
8990             cmd.append(getNativePath(fullName));
8991             cmd.append(" ");
8992             cmd.append(getNativePath(symFullName));
8993             if (!executeCommand(cmd, "", outbuf, errbuf))
8994                 {
8995                 error("<strip> symbol file failed : %s", errbuf.c_str());
8996                 return false;
8997                 }
8998             }
8999             
9000         cmd = command;
9001         cmd.append(getNativePath(fullName));
9002         if (!executeCommand(cmd, "", outbuf, errbuf))
9003             {
9004             error("<strip> failed : %s", errbuf.c_str());
9005             return false;
9006             }
9007         removeFromStatCache(getNativePath(fullName));
9008         return true;
9009         }
9011     virtual bool parse(Element *elem)
9012         {
9013         if (!parent.getAttribute(elem, "command", commandOpt))
9014             return false;
9015         if (!parent.getAttribute(elem, "file", fileNameOpt))
9016             return false;
9017         if (!parent.getAttribute(elem, "symfile", symFileNameOpt))
9018             return false;
9019         if (fileNameOpt.size() == 0)
9020             {
9021             error("<strip> requires 'file=\"fileName\"' attribute");
9022             return false;
9023             }
9024         return true;
9025         }
9027 private:
9029     String commandOpt;
9030     String fileNameOpt;
9031     String symFileNameOpt;
9032 };
9035 /**
9036  *
9037  */
9038 class TaskTouch : public Task
9040 public:
9042     TaskTouch(MakeBase &par) : Task(par)
9043         { type = TASK_TOUCH; name = "touch"; }
9045     virtual ~TaskTouch()
9046         {}
9048     virtual bool execute()
9049         {
9050         String fileName = parent.eval(fileNameOpt, "");
9052         String fullName = parent.resolve(fileName);
9053         String nativeFile = getNativePath(fullName);
9054         if (!isRegularFile(fullName) && !isDirectory(fullName))
9055             {            
9056             // S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH
9057             int ret = creat(nativeFile.c_str(), 0666);
9058             if (ret != 0) 
9059                 {
9060                 error("<touch> could not create '%s' : %s",
9061                     nativeFile.c_str(), strerror(ret));
9062                 return false;
9063                 }
9064             return true;
9065             }
9066         int ret = utime(nativeFile.c_str(), (struct utimbuf *)0);
9067         if (ret != 0)
9068             {
9069             error("<touch> could not update the modification time for '%s' : %s",
9070                 nativeFile.c_str(), strerror(ret));
9071             return false;
9072             }
9073         removeFromStatCache(nativeFile);
9074         return true;
9075         }
9077     virtual bool parse(Element *elem)
9078         {
9079         //trace("touch parse");
9080         if (!parent.getAttribute(elem, "file", fileNameOpt))
9081             return false;
9082         if (fileNameOpt.size() == 0)
9083             {
9084             error("<touch> requires 'file=\"fileName\"' attribute");
9085             return false;
9086             }
9087         return true;
9088         }
9090     String fileNameOpt;
9091 };
9094 /**
9095  *
9096  */
9097 class TaskTstamp : public Task
9099 public:
9101     TaskTstamp(MakeBase &par) : Task(par)
9102         { type = TASK_TSTAMP; name = "tstamp"; }
9104     virtual ~TaskTstamp()
9105         {}
9107     virtual bool execute()
9108         {
9109         return true;
9110         }
9112     virtual bool parse(Element *elem)
9113         {
9114         //trace("tstamp parse");
9115         return true;
9116         }
9117 };
9121 /**
9122  *
9123  */
9124 Task *Task::createTask(Element *elem, int lineNr)
9126     String tagName = elem->getName();
9127     //trace("task:%s", tagName.c_str());
9128     Task *task = NULL;
9129     if (tagName == "cc")
9130         task = new TaskCC(parent);
9131     else if (tagName == "copy")
9132         task = new TaskCopy(parent);
9133     else if (tagName == "cxxtestpart")
9134         task = new TaskCxxTestPart(parent);
9135     else if (tagName == "cxxtestroot")
9136         task = new TaskCxxTestRoot(parent);
9137     else if (tagName == "cxxtestrun")
9138         task = new TaskCxxTestRun(parent);
9139     else if (tagName == "delete")
9140         task = new TaskDelete(parent);
9141     else if (tagName == "echo")
9142         task = new TaskEcho(parent);
9143     else if (tagName == "jar")
9144         task = new TaskJar(parent);
9145     else if (tagName == "javac")
9146         task = new TaskJavac(parent);
9147     else if (tagName == "link")
9148         task = new TaskLink(parent);
9149     else if (tagName == "makefile")
9150         task = new TaskMakeFile(parent);
9151     else if (tagName == "mkdir")
9152         task = new TaskMkDir(parent);
9153     else if (tagName == "msgfmt")
9154         task = new TaskMsgFmt(parent);
9155     else if (tagName == "pkg-config")
9156         task = new TaskPkgConfig(parent);
9157     else if (tagName == "ranlib")
9158         task = new TaskRanlib(parent);
9159     else if (tagName == "rc")
9160         task = new TaskRC(parent);
9161     else if (tagName == "sharedlib")
9162         task = new TaskSharedLib(parent);
9163     else if (tagName == "staticlib")
9164         task = new TaskStaticLib(parent);
9165     else if (tagName == "strip")
9166         task = new TaskStrip(parent);
9167     else if (tagName == "touch")
9168         task = new TaskTouch(parent);
9169     else if (tagName == "tstamp")
9170         task = new TaskTstamp(parent);
9171     else
9172         {
9173         error("Unknown task '%s'", tagName.c_str());
9174         return NULL;
9175         }
9177     task->setLine(lineNr);
9179     if (!task->parse(elem))
9180         {
9181         delete task;
9182         return NULL;
9183         }
9184     return task;
9189 //########################################################################
9190 //# T A R G E T
9191 //########################################################################
9193 /**
9194  *
9195  */
9196 class Target : public MakeBase
9199 public:
9201     /**
9202      *
9203      */
9204     Target(Make &par) : parent(par)
9205         { init(); }
9207     /**
9208      *
9209      */
9210     Target(const Target &other) : parent(other.parent)
9211         { init(); assign(other); }
9213     /**
9214      *
9215      */
9216     Target &operator=(const Target &other)
9217         { init(); assign(other); return *this; }
9219     /**
9220      *
9221      */
9222     virtual ~Target()
9223         { cleanup() ; }
9226     /**
9227      *
9228      */
9229     virtual Make &getParent()
9230         { return parent; }
9232     /**
9233      *
9234      */
9235     virtual String getName()
9236         { return name; }
9238     /**
9239      *
9240      */
9241     virtual void setName(const String &val)
9242         { name = val; }
9244     /**
9245      *
9246      */
9247     virtual String getDescription()
9248         { return description; }
9250     /**
9251      *
9252      */
9253     virtual void setDescription(const String &val)
9254         { description = val; }
9256     /**
9257      *
9258      */
9259     virtual void addDependency(const String &val)
9260         { deps.push_back(val); }
9262     /**
9263      *
9264      */
9265     virtual void parseDependencies(const String &val)
9266         { deps = tokenize(val, ", "); }
9268     /**
9269      *
9270      */
9271     virtual std::vector<String> &getDependencies()
9272         { return deps; }
9274     /**
9275      *
9276      */
9277     virtual String getIf()
9278         { return ifVar; }
9280     /**
9281      *
9282      */
9283     virtual void setIf(const String &val)
9284         { ifVar = val; }
9286     /**
9287      *
9288      */
9289     virtual String getUnless()
9290         { return unlessVar; }
9292     /**
9293      *
9294      */
9295     virtual void setUnless(const String &val)
9296         { unlessVar = val; }
9298     /**
9299      *
9300      */
9301     virtual void addTask(Task *val)
9302         { tasks.push_back(val); }
9304     /**
9305      *
9306      */
9307     virtual std::vector<Task *> &getTasks()
9308         { return tasks; }
9310 private:
9312     void init()
9313         {
9314         }
9316     void cleanup()
9317         {
9318         tasks.clear();
9319         }
9321     void assign(const Target &other)
9322         {
9323         //parent      = other.parent;
9324         name        = other.name;
9325         description = other.description;
9326         ifVar       = other.ifVar;
9327         unlessVar   = other.unlessVar;
9328         deps        = other.deps;
9329         tasks       = other.tasks;
9330         }
9332     Make &parent;
9334     String name;
9336     String description;
9338     String ifVar;
9340     String unlessVar;
9342     std::vector<String> deps;
9344     std::vector<Task *> tasks;
9346 };
9355 //########################################################################
9356 //# M A K E
9357 //########################################################################
9360 /**
9361  *
9362  */
9363 class Make : public MakeBase
9366 public:
9368     /**
9369      *
9370      */
9371     Make()
9372         { init(); }
9374     /**
9375      *
9376      */
9377     Make(const Make &other)
9378         { assign(other); }
9380     /**
9381      *
9382      */
9383     Make &operator=(const Make &other)
9384         { assign(other); return *this; }
9386     /**
9387      *
9388      */
9389     virtual ~Make()
9390         { cleanup(); }
9392     /**
9393      *
9394      */
9395     virtual std::map<String, Target> &getTargets()
9396         { return targets; }
9399     /**
9400      *
9401      */
9402     virtual String version()
9403         { return BUILDTOOL_VERSION; }
9405     /**
9406      * Overload a <property>
9407      */
9408     virtual bool specifyProperty(const String &name,
9409                                  const String &value);
9411     /**
9412      *
9413      */
9414     virtual bool run();
9416     /**
9417      *
9418      */
9419     virtual bool run(const String &target);
9423 private:
9425     /**
9426      *
9427      */
9428     void init();
9430     /**
9431      *
9432      */
9433     void cleanup();
9435     /**
9436      *
9437      */
9438     void assign(const Make &other);
9440     /**
9441      *
9442      */
9443     bool executeTask(Task &task);
9446     /**
9447      *
9448      */
9449     bool executeTarget(Target &target,
9450              std::set<String> &targetsCompleted);
9453     /**
9454      *
9455      */
9456     bool execute();
9458     /**
9459      *
9460      */
9461     bool checkTargetDependencies(Target &prop,
9462                     std::vector<String> &depList);
9464     /**
9465      *
9466      */
9467     bool parsePropertyFile(const String &fileName,
9468                            const String &prefix);
9470     /**
9471      *
9472      */
9473     bool parseProperty(Element *elem);
9475     /**
9476      *
9477      */
9478     bool parseFile();
9480     /**
9481      *
9482      */
9483     std::vector<String> glob(const String &pattern);
9486     //###############
9487     //# Fields
9488     //###############
9490     String projectName;
9492     String currentTarget;
9494     String defaultTarget;
9496     String specifiedTarget;
9498     String baseDir;
9500     String description;
9501     
9502     //std::vector<Property> properties;
9503     
9504     std::map<String, Target> targets;
9506     std::vector<Task *> allTasks;
9507     
9508     std::map<String, String> specifiedProperties;
9510 };
9513 //########################################################################
9514 //# C L A S S  M A I N T E N A N C E
9515 //########################################################################
9517 /**
9518  *
9519  */
9520 void Make::init()
9522     uri             = "build.xml";
9523     projectName     = "";
9524     currentTarget   = "";
9525     defaultTarget   = "";
9526     specifiedTarget = "";
9527     baseDir         = "";
9528     description     = "";
9529     envPrefix       = "env.";
9530     pcPrefix        = "pc.";
9531     pccPrefix       = "pcc.";
9532     pclPrefix       = "pcl.";
9533     bzrPrefix       = "bzr.";
9534     properties.clear();
9535     for (unsigned int i = 0 ; i < allTasks.size() ; i++)
9536         delete allTasks[i];
9537     allTasks.clear();
9542 /**
9543  *
9544  */
9545 void Make::cleanup()
9547     for (unsigned int i = 0 ; i < allTasks.size() ; i++)
9548         delete allTasks[i];
9549     allTasks.clear();
9554 /**
9555  *
9556  */
9557 void Make::assign(const Make &other)
9559     uri              = other.uri;
9560     projectName      = other.projectName;
9561     currentTarget    = other.currentTarget;
9562     defaultTarget    = other.defaultTarget;
9563     specifiedTarget  = other.specifiedTarget;
9564     baseDir          = other.baseDir;
9565     description      = other.description;
9566     properties       = other.properties;
9571 //########################################################################
9572 //# U T I L I T Y    T A S K S
9573 //########################################################################
9575 /**
9576  *  Perform a file globbing
9577  */
9578 std::vector<String> Make::glob(const String &pattern)
9580     std::vector<String> res;
9581     return res;
9585 //########################################################################
9586 //# P U B L I C    A P I
9587 //########################################################################
9591 /**
9592  *
9593  */
9594 bool Make::executeTarget(Target &target,
9595              std::set<String> &targetsCompleted)
9598     String name = target.getName();
9600     //First get any dependencies for this target
9601     std::vector<String> deps = target.getDependencies();
9602     for (unsigned int i=0 ; i<deps.size() ; i++)
9603         {
9604         String dep = deps[i];
9605         //Did we do it already?  Skip
9606         if (targetsCompleted.find(dep)!=targetsCompleted.end())
9607             continue;
9608             
9609         std::map<String, Target> &tgts =
9610                target.getParent().getTargets();
9611         std::map<String, Target>::iterator iter =
9612                tgts.find(dep);
9613         if (iter == tgts.end())
9614             {
9615             error("Target '%s' dependency '%s' not found",
9616                       name.c_str(),  dep.c_str());
9617             return false;
9618             }
9619         Target depTarget = iter->second;
9620         if (!executeTarget(depTarget, targetsCompleted))
9621             {
9622             return false;
9623             }
9624         }
9626     status("##### Target : %s\n##### %s", name.c_str(),
9627             target.getDescription().c_str());
9629     //Now let's do the tasks
9630     std::vector<Task *> &tasks = target.getTasks();
9631     for (unsigned int i=0 ; i<tasks.size() ; i++)
9632         {
9633         Task *task = tasks[i];
9634         status("--- %s / %s", name.c_str(), task->getName().c_str());
9635         if (!task->execute())
9636             {
9637             return false;
9638             }
9639         }
9640         
9641     targetsCompleted.insert(name);
9642     
9643     return true;
9648 /**
9649  *  Main execute() method.  Start here and work
9650  *  up the dependency tree 
9651  */
9652 bool Make::execute()
9654     status("######## EXECUTE");
9656     //Determine initial target
9657     if (specifiedTarget.size()>0)
9658         {
9659         currentTarget = specifiedTarget;
9660         }
9661     else if (defaultTarget.size()>0)
9662         {
9663         currentTarget = defaultTarget;
9664         }
9665     else
9666         {
9667         error("execute: no specified or default target requested");
9668         return false;
9669         }
9671     std::map<String, Target>::iterator iter =
9672                targets.find(currentTarget);
9673     if (iter == targets.end())
9674         {
9675         error("Initial target '%s' not found",
9676                  currentTarget.c_str());
9677         return false;
9678         }
9679         
9680     //Now run
9681     Target target = iter->second;
9682     std::set<String> targetsCompleted;
9683     if (!executeTarget(target, targetsCompleted))
9684         {
9685         return false;
9686         }
9688     status("######## EXECUTE COMPLETE");
9689     return true;
9695 /**
9696  *
9697  */
9698 bool Make::checkTargetDependencies(Target &target, 
9699                             std::vector<String> &depList)
9701     String tgtName = target.getName().c_str();
9702     depList.push_back(tgtName);
9704     std::vector<String> deps = target.getDependencies();
9705     for (unsigned int i=0 ; i<deps.size() ; i++)
9706         {
9707         String dep = deps[i];
9708         //First thing entered was the starting Target
9709         if (dep == depList[0])
9710             {
9711             error("Circular dependency '%s' found at '%s'",
9712                       dep.c_str(), tgtName.c_str());
9713             std::vector<String>::iterator diter;
9714             for (diter=depList.begin() ; diter!=depList.end() ; diter++)
9715                 {
9716                 error("  %s", diter->c_str());
9717                 }
9718             return false;
9719             }
9721         std::map<String, Target> &tgts =
9722                   target.getParent().getTargets();
9723         std::map<String, Target>::iterator titer = tgts.find(dep);
9724         if (titer == tgts.end())
9725             {
9726             error("Target '%s' dependency '%s' not found",
9727                       tgtName.c_str(), dep.c_str());
9728             return false;
9729             }
9730         if (!checkTargetDependencies(titer->second, depList))
9731             {
9732             return false;
9733             }
9734         }
9735     return true;
9742 static int getword(int pos, const String &inbuf, String &result)
9744     int p = pos;
9745     int len = (int)inbuf.size();
9746     String val;
9747     while (p < len)
9748         {
9749         char ch = inbuf[p];
9750         if (!isalnum(ch) && ch!='.' && ch!='_')
9751             break;
9752         val.push_back(ch);
9753         p++;
9754         }
9755     result = val;
9756     return p;
9762 /**
9763  *
9764  */
9765 bool Make::parsePropertyFile(const String &fileName,
9766                              const String &prefix)
9768     FILE *f = fopen(fileName.c_str(), "r");
9769     if (!f)
9770         {
9771         error("could not open property file %s", fileName.c_str());
9772         return false;
9773         }
9774     int linenr = 0;
9775     while (!feof(f))
9776         {
9777         char buf[256];
9778         if (!fgets(buf, 255, f))
9779             break;
9780         linenr++;
9781         String s = buf;
9782         s = trim(s);
9783         int len = s.size();
9784         if (len == 0)
9785             continue;
9786         if (s[0] == '#')
9787             continue;
9788         String key;
9789         String val;
9790         int p = 0;
9791         int p2 = getword(p, s, key);
9792         if (p2 <= p)
9793             {
9794             error("property file %s, line %d: expected keyword",
9795                     fileName.c_str(), linenr);
9796             return false;
9797             }
9798         if (prefix.size() > 0)
9799             {
9800             key.insert(0, prefix);
9801             }
9803         //skip whitespace
9804         for (p=p2 ; p<len ; p++)
9805             if (!isspace(s[p]))
9806                 break;
9808         if (p>=len || s[p]!='=')
9809             {
9810             error("property file %s, line %d: expected '='",
9811                     fileName.c_str(), linenr);
9812             return false;
9813             }
9814         p++;
9816         //skip whitespace
9817         for ( ; p<len ; p++)
9818             if (!isspace(s[p]))
9819                 break;
9821         /* This way expects a word after the =
9822         p2 = getword(p, s, val);
9823         if (p2 <= p)
9824             {
9825             error("property file %s, line %d: expected value",
9826                     fileName.c_str(), linenr);
9827             return false;
9828             }
9829         */
9830         // This way gets the rest of the line after the =
9831         if (p>=len)
9832             {
9833             error("property file %s, line %d: expected value",
9834                     fileName.c_str(), linenr);
9835             return false;
9836             }
9837         val = s.substr(p);
9838         if (key.size()==0)
9839             continue;
9840         //allow property to be set, even if val=""
9842         //trace("key:'%s' val:'%s'", key.c_str(), val.c_str());
9843         //See if we wanted to overload this property
9844         std::map<String, String>::iterator iter =
9845             specifiedProperties.find(key);
9846         if (iter!=specifiedProperties.end())
9847             {
9848             val = iter->second;
9849             status("overloading property '%s' = '%s'",
9850                    key.c_str(), val.c_str());
9851             }
9852         properties[key] = val;
9853         }
9854     fclose(f);
9855     return true;
9861 /**
9862  *
9863  */
9864 bool Make::parseProperty(Element *elem)
9866     std::vector<Attribute> &attrs = elem->getAttributes();
9867     for (unsigned int i=0 ; i<attrs.size() ; i++)
9868         {
9869         String attrName = attrs[i].getName();
9870         String attrVal  = attrs[i].getValue();
9872         if (attrName == "name")
9873             {
9874             String val;
9875             if (!getAttribute(elem, "value", val))
9876                 return false;
9877             if (val.size() > 0)
9878                 {
9879                 properties[attrVal] = val;
9880                 }
9881             else
9882                 {
9883                 if (!getAttribute(elem, "location", val))
9884                     return false;
9885                 //let the property exist, even if not defined
9886                 properties[attrVal] = val;
9887                 }
9888             //See if we wanted to overload this property
9889             std::map<String, String>::iterator iter =
9890                 specifiedProperties.find(attrVal);
9891             if (iter != specifiedProperties.end())
9892                 {
9893                 val = iter->second;
9894                 status("overloading property '%s' = '%s'",
9895                     attrVal.c_str(), val.c_str());
9896                 properties[attrVal] = val;
9897                 }
9898             }
9899         else if (attrName == "file")
9900             {
9901             String prefix;
9902             if (!getAttribute(elem, "prefix", prefix))
9903                 return false;
9904             if (prefix.size() > 0)
9905                 {
9906                 if (prefix[prefix.size()-1] != '.')
9907                     prefix.push_back('.');
9908                 }
9909             if (!parsePropertyFile(attrName, prefix))
9910                 return false;
9911             }
9912         else if (attrName == "environment")
9913             {
9914             if (attrVal.find('.') != attrVal.npos)
9915                 {
9916                 error("environment prefix cannot have a '.' in it");
9917                 return false;
9918                 }
9919             envPrefix = attrVal;
9920             envPrefix.push_back('.');
9921             }
9922         else if (attrName == "pkg-config")
9923             {
9924             if (attrVal.find('.') != attrVal.npos)
9925                 {
9926                 error("pkg-config prefix cannot have a '.' in it");
9927                 return false;
9928                 }
9929             pcPrefix = attrVal;
9930             pcPrefix.push_back('.');
9931             }
9932         else if (attrName == "pkg-config-cflags")
9933             {
9934             if (attrVal.find('.') != attrVal.npos)
9935                 {
9936                 error("pkg-config-cflags prefix cannot have a '.' in it");
9937                 return false;
9938                 }
9939             pccPrefix = attrVal;
9940             pccPrefix.push_back('.');
9941             }
9942         else if (attrName == "pkg-config-libs")
9943             {
9944             if (attrVal.find('.') != attrVal.npos)
9945                 {
9946                 error("pkg-config-libs prefix cannot have a '.' in it");
9947                 return false;
9948                 }
9949             pclPrefix = attrVal;
9950             pclPrefix.push_back('.');
9951             }
9952         else if (attrName == "subversion")
9953             {
9954             if (attrVal.find('.') != attrVal.npos)
9955                 {
9956                 error("bzr prefix cannot have a '.' in it");
9957                 return false;
9958                 }
9959             bzrPrefix = attrVal;
9960             bzrPrefix.push_back('.');
9961             }
9962         }
9964     return true;
9970 /**
9971  *
9972  */
9973 bool Make::parseFile()
9975     status("######## PARSE : %s", uri.getPath().c_str());
9977     setLine(0);
9979     Parser parser;
9980     Element *root = parser.parseFile(uri.getNativePath());
9981     if (!root)
9982         {
9983         error("Could not open %s for reading",
9984               uri.getNativePath().c_str());
9985         return false;
9986         }
9987     
9988     setLine(root->getLine());
9990     if (root->getChildren().size()==0 ||
9991         root->getChildren()[0]->getName()!="project")
9992         {
9993         error("Main xml element should be <project>");
9994         delete root;
9995         return false;
9996         }
9998     //########## Project attributes
9999     Element *project = root->getChildren()[0];
10000     String s = project->getAttribute("name");
10001     if (s.size() > 0)
10002         projectName = s;
10003     s = project->getAttribute("default");
10004     if (s.size() > 0)
10005         defaultTarget = s;
10006     s = project->getAttribute("basedir");
10007     if (s.size() > 0)
10008         baseDir = s;
10010     //######### PARSE MEMBERS
10011     std::vector<Element *> children = project->getChildren();
10012     for (unsigned int i=0 ; i<children.size() ; i++)
10013         {
10014         Element *elem = children[i];
10015         setLine(elem->getLine());
10016         String tagName = elem->getName();
10018         //########## DESCRIPTION
10019         if (tagName == "description")
10020             {
10021             description = parser.trim(elem->getValue());
10022             }
10024         //######### PROPERTY
10025         else if (tagName == "property")
10026             {
10027             if (!parseProperty(elem))
10028                 return false;
10029             }
10031         //######### TARGET
10032         else if (tagName == "target")
10033             {
10034             String tname   = elem->getAttribute("name");
10035             String tdesc   = elem->getAttribute("description");
10036             String tdeps   = elem->getAttribute("depends");
10037             String tif     = elem->getAttribute("if");
10038             String tunless = elem->getAttribute("unless");
10039             Target target(*this);
10040             target.setName(tname);
10041             target.setDescription(tdesc);
10042             target.parseDependencies(tdeps);
10043             target.setIf(tif);
10044             target.setUnless(tunless);
10045             std::vector<Element *> telems = elem->getChildren();
10046             for (unsigned int i=0 ; i<telems.size() ; i++)
10047                 {
10048                 Element *telem = telems[i];
10049                 Task breeder(*this);
10050                 Task *task = breeder.createTask(telem, telem->getLine());
10051                 if (!task)
10052                     return false;
10053                 allTasks.push_back(task);
10054                 target.addTask(task);
10055                 }
10057             //Check name
10058             if (tname.size() == 0)
10059                 {
10060                 error("no name for target");
10061                 return false;
10062                 }
10063             //Check for duplicate name
10064             if (targets.find(tname) != targets.end())
10065                 {
10066                 error("target '%s' already defined", tname.c_str());
10067                 return false;
10068                 }
10069             //more work than targets[tname]=target, but avoids default allocator
10070             targets.insert(std::make_pair<String, Target>(tname, target));
10071             }
10072         //######### none of the above
10073         else
10074             {
10075             error("unknown toplevel tag: <%s>", tagName.c_str());
10076             return false;
10077             }
10079         }
10081     std::map<String, Target>::iterator iter;
10082     for (iter = targets.begin() ; iter!= targets.end() ; iter++)
10083         {
10084         Target tgt = iter->second;
10085         std::vector<String> depList;
10086         if (!checkTargetDependencies(tgt, depList))
10087             {
10088             return false;
10089             }
10090         }
10093     delete root;
10094     status("######## PARSE COMPLETE");
10095     return true;
10099 /**
10100  * Overload a <property>
10101  */
10102 bool Make::specifyProperty(const String &name, const String &value)
10104     if (specifiedProperties.find(name) != specifiedProperties.end())
10105         {
10106         error("Property %s already specified", name.c_str());
10107         return false;
10108         }
10109     specifiedProperties[name] = value;
10110     return true;
10115 /**
10116  *
10117  */
10118 bool Make::run()
10120     if (!parseFile())
10121         return false;
10122         
10123     if (!execute())
10124         return false;
10126     return true;
10132 /**
10133  * Get a formatted MM:SS.sss time elapsed string
10134  */ 
10135 static String
10136 timeDiffString(struct timeval &x, struct timeval &y)
10138     long microsX  = x.tv_usec;
10139     long secondsX = x.tv_sec;
10140     long microsY  = y.tv_usec;
10141     long secondsY = y.tv_sec;
10142     if (microsX < microsY)
10143         {
10144         microsX += 1000000;
10145         secondsX -= 1;
10146         }
10148     int seconds = (int)(secondsX - secondsY);
10149     int millis  = (int)((microsX - microsY)/1000);
10151     int minutes = seconds/60;
10152     seconds -= minutes*60;
10153     char buf[80];
10154     snprintf(buf, 79, "%dm %d.%03ds", minutes, seconds, millis);
10155     String ret = buf;
10156     return ret;
10157     
10160 /**
10161  *
10162  */
10163 bool Make::run(const String &target)
10165     status("####################################################");
10166     status("#   %s", version().c_str());
10167     status("####################################################");
10168     struct timeval timeStart, timeEnd;
10169     ::gettimeofday(&timeStart, NULL);
10170     specifiedTarget = target;
10171     if (!run())
10172         return false;
10173     ::gettimeofday(&timeEnd, NULL);
10174     String timeStr = timeDiffString(timeEnd, timeStart);
10175     status("####################################################");
10176     status("#   BuildTool Completed : %s", timeStr.c_str());
10177     status("####################################################");
10178     return true;
10187 }// namespace buildtool
10188 //########################################################################
10189 //# M A I N
10190 //########################################################################
10192 typedef buildtool::String String;
10194 /**
10195  *  Format an error message in printf() style
10196  */
10197 static void error(const char *fmt, ...)
10199     va_list ap;
10200     va_start(ap, fmt);
10201     fprintf(stderr, "BuildTool error: ");
10202     vfprintf(stderr, fmt, ap);
10203     fprintf(stderr, "\n");
10204     va_end(ap);
10208 static bool parseProperty(const String &s, String &name, String &val)
10210     int len = s.size();
10211     int i;
10212     for (i=0 ; i<len ; i++)
10213         {
10214         char ch = s[i];
10215         if (ch == '=')
10216             break;
10217         name.push_back(ch);
10218         }
10219     if (i>=len || s[i]!='=')
10220         {
10221         error("property requires -Dname=value");
10222         return false;
10223         }
10224     i++;
10225     for ( ; i<len ; i++)
10226         {
10227         char ch = s[i];
10228         val.push_back(ch);
10229         }
10230     return true;
10234 /**
10235  * Compare a buffer with a key, for the length of the key
10236  */
10237 static bool sequ(const String &buf, const char *key)
10239     int len = buf.size();
10240     for (int i=0 ; key[i] && i<len ; i++)
10241         {
10242         if (key[i] != buf[i])
10243             return false;
10244         }        
10245     return true;
10248 static void usage(int argc, char **argv)
10250     printf("usage:\n");
10251     printf("   %s [options] [target]\n", argv[0]);
10252     printf("Options:\n");
10253     printf("  -help, -h              print this message\n");
10254     printf("  -version               print the version information and exit\n");
10255     printf("  -file <file>           use given buildfile\n");
10256     printf("  -f <file>                 ''\n");
10257     printf("  -D<property>=<value>   use value for given property\n");
10263 /**
10264  * Parse the command-line args, get our options,
10265  * and run this thing
10266  */   
10267 static bool parseOptions(int argc, char **argv)
10269     if (argc < 1)
10270         {
10271         error("Cannot parse arguments");
10272         return false;
10273         }
10275     buildtool::Make make;
10277     String target;
10279     //char *progName = argv[0];
10280     for (int i=1 ; i<argc ; i++)
10281         {
10282         String arg = argv[i];
10283         if (arg.size()>1 && arg[0]=='-')
10284             {
10285             if (arg == "-h" || arg == "-help")
10286                 {
10287                 usage(argc,argv);
10288                 return true;
10289                 }
10290             else if (arg == "-version")
10291                 {
10292                 printf("%s", make.version().c_str());
10293                 return true;
10294                 }
10295             else if (arg == "-f" || arg == "-file")
10296                 {
10297                 if (i>=argc)
10298                    {
10299                    usage(argc, argv);
10300                    return false;
10301                    }
10302                 i++; //eat option
10303                 make.setURI(argv[i]);
10304                 }
10305             else if (arg.size()>2 && sequ(arg, "-D"))
10306                 {
10307                 String s = arg.substr(2, arg.size());
10308                 String name, value;
10309                 if (!parseProperty(s, name, value))
10310                    {
10311                    usage(argc, argv);
10312                    return false;
10313                    }
10314                 if (!make.specifyProperty(name, value))
10315                     return false;
10316                 }
10317             else
10318                 {
10319                 error("Unknown option:%s", arg.c_str());
10320                 return false;
10321                 }
10322             }
10323         else
10324             {
10325             if (target.size()>0)
10326                 {
10327                 error("only one initial target");
10328                 usage(argc, argv);
10329                 return false;
10330                 }
10331             target = arg;
10332             }
10333         }
10335     //We have the options.  Now execute them
10336     if (!make.run(target))
10337         return false;
10339     return true;
10346 static bool runMake()
10348     buildtool::Make make;
10349     if (!make.run())
10350         return false;
10351     return true;
10355 static bool pkgConfigTest()
10357     buildtool::PkgConfig pkgConfig;
10358     if (!pkgConfig.readFile("gtk+-2.0.pc"))
10359         return false;
10360     return true;
10365 static bool depTest()
10367     buildtool::DepTool deptool;
10368     deptool.setSourceDirectory("/dev/ink/inkscape/src");
10369     if (!deptool.generateDependencies("build.dep"))
10370         return false;
10371     std::vector<buildtool::FileRec> res =
10372            deptool.loadDepFile("build.dep");
10373     if (res.size() == 0)
10374         return false;
10375     return true;
10378 static bool popenTest()
10380     buildtool::Make make;
10381     buildtool::String out, err;
10382     bool ret = make.executeCommand("gcc xx.cpp", "", out, err);
10383     printf("Popen test:%d '%s' '%s'\n", ret, out.c_str(), err.c_str());
10384     return true;
10388 static bool propFileTest()
10390     buildtool::Make make;
10391     make.parsePropertyFile("test.prop", "test.");
10392     return true;
10396 int main(int argc, char **argv)
10399     if (!parseOptions(argc, argv))
10400         return 1;
10401     /*
10402     if (!popenTest())
10403         return 1;
10405     if (!depTest())
10406         return 1;
10407     if (!propFileTest())
10408         return 1;
10409     if (runMake())
10410         return 1;
10411     */
10412     return 0;
10416 //########################################################################
10417 //# E N D 
10418 //########################################################################