Code

include <algorithm> for std::sort
[inkscape.git] / buildtool.cpp
1 /**
2  * Simple build automation tool.
3  *
4  * Authors:
5  *   Bob Jamison
6  *
7  * Copyright (C) 2006-2008 Bob Jamison
8  *
9  *  This library is free software; you can redistribute it and/or
10  *  modify it under the terms of the GNU Lesser General Public
11  *  License as published by the Free Software Foundation; either
12  *  version 2.1 of the License, or (at your option) any later version.
13  *
14  *  This library is distributed in the hope that it will be useful,
15  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  *  Lesser General Public License for more details.
18  *
19  *  You should have received a copy of the GNU Lesser General Public
20  *  License along with this library; if not, write to the Free Software
21  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
22  */
24 /**
25  * To use this file, compile with:
26  * <pre>
27  * g++ -O3 buildtool.cpp -o btool.exe
28  * (or whatever your compiler might be)
29  * Then
30  * btool
31  * or
32  * btool {target}
33  *
34  * Note: if you are using MinGW, and a not very recent version of it,
35  * gettimeofday() might be missing.  If so, just build this file with
36  * this command:
37  * g++ -O3 -DNEED_GETTIMEOFDAY buildtool.cpp -o btool.exe
38  *
39  */
41 #define BUILDTOOL_VERSION  "BuildTool v0.9.4"
43 #include <stdio.h>
44 #include <fcntl.h>
45 #include <unistd.h>
46 #include <stdarg.h>
47 #include <sys/stat.h>
48 #include <time.h>
49 #include <sys/time.h>
50 #include <utime.h>
51 #include <dirent.h>
53 #include <string>
54 #include <map>
55 #include <set>
56 #include <vector>
57 #include <algorithm>
60 #ifdef __WIN32__
61 #include <windows.h>
62 #endif
65 #include <errno.h>
68 //########################################################################
69 //# Definition of gettimeofday() for those who don't have it
70 //########################################################################
71 #ifdef NEED_GETTIMEOFDAY
72 #include <sys/timeb.h>
74 struct timezone {
75       int tz_minuteswest; /* minutes west of Greenwich */
76       int tz_dsttime;     /* type of dst correction */
77     };
79 static int gettimeofday (struct timeval *tv, struct timezone *tz)
80 {
81    struct _timeb tb;
83    if (!tv)
84       return (-1);
86     _ftime (&tb);
87     tv->tv_sec  = tb.time;
88     tv->tv_usec = tb.millitm * 1000 + 500;
89     if (tz)
90         {
91         tz->tz_minuteswest = -60 * _timezone;
92         tz->tz_dsttime = _daylight;
93         }
94     return 0;
95 }
97 #endif
105 namespace buildtool
111 //########################################################################
112 //########################################################################
113 //##  R E G E X P
114 //########################################################################
115 //########################################################################
117 /**
118  * This is the T-Rex regular expression library, which we
119  * gratefully acknowledge.  It's clean code and small size allow
120  * us to embed it in BuildTool without adding a dependency
121  *
122  */    
124 //begin trex.h
126 #ifndef _TREX_H_
127 #define _TREX_H_
128 /***************************************************************
129     T-Rex a tiny regular expression library
131     Copyright (C) 2003-2006 Alberto Demichelis
133     This software is provided 'as-is', without any express 
134     or implied warranty. In no event will the authors be held 
135     liable for any damages arising from the use of this software.
137     Permission is granted to anyone to use this software for 
138     any purpose, including commercial applications, and to alter
139     it and redistribute it freely, subject to the following restrictions:
141         1. The origin of this software must not be misrepresented;
142         you must not claim that you wrote the original software.
143         If you use this software in a product, an acknowledgment
144         in the product documentation would be appreciated but
145         is not required.
147         2. Altered source versions must be plainly marked as such,
148         and must not be misrepresented as being the original software.
150         3. This notice may not be removed or altered from any
151         source distribution.
153 ****************************************************************/
155 #ifdef _UNICODE
156 #define TRexChar unsigned short
157 #define MAX_CHAR 0xFFFF
158 #define _TREXC(c) L##c 
159 #define trex_strlen wcslen
160 #define trex_printf wprintf
161 #else
162 #define TRexChar char
163 #define MAX_CHAR 0xFF
164 #define _TREXC(c) (c) 
165 #define trex_strlen strlen
166 #define trex_printf printf
167 #endif
169 #ifndef TREX_API
170 #define TREX_API extern
171 #endif
173 #define TRex_True 1
174 #define TRex_False 0
176 typedef unsigned int TRexBool;
177 typedef struct TRex TRex;
179 typedef struct {
180     const TRexChar *begin;
181     int len;
182 } TRexMatch;
184 TREX_API TRex *trex_compile(const TRexChar *pattern,const TRexChar **error);
185 TREX_API void trex_free(TRex *exp);
186 TREX_API TRexBool trex_match(TRex* exp,const TRexChar* text);
187 TREX_API TRexBool trex_search(TRex* exp,const TRexChar* text, const TRexChar** out_begin, const TRexChar** out_end);
188 TREX_API TRexBool trex_searchrange(TRex* exp,const TRexChar* text_begin,const TRexChar* text_end,const TRexChar** out_begin, const TRexChar** out_end);
189 TREX_API int trex_getsubexpcount(TRex* exp);
190 TREX_API TRexBool trex_getsubexp(TRex* exp, int n, TRexMatch *subexp);
192 #endif
194 //end trex.h
196 //start trex.c
199 #include <stdio.h>
200 #include <string>
202 /* see copyright notice in trex.h */
203 #include <string.h>
204 #include <stdlib.h>
205 #include <ctype.h>
206 #include <setjmp.h>
207 //#include "trex.h"
209 #ifdef _UINCODE
210 #define scisprint iswprint
211 #define scstrlen wcslen
212 #define scprintf wprintf
213 #define _SC(x) L(x)
214 #else
215 #define scisprint isprint
216 #define scstrlen strlen
217 #define scprintf printf
218 #define _SC(x) (x)
219 #endif
221 #ifdef _DEBUG
222 #include <stdio.h>
224 static const TRexChar *g_nnames[] =
226     _SC("NONE"),_SC("OP_GREEDY"),    _SC("OP_OR"),
227     _SC("OP_EXPR"),_SC("OP_NOCAPEXPR"),_SC("OP_DOT"),    _SC("OP_CLASS"),
228     _SC("OP_CCLASS"),_SC("OP_NCLASS"),_SC("OP_RANGE"),_SC("OP_CHAR"),
229     _SC("OP_EOL"),_SC("OP_BOL"),_SC("OP_WB")
230 };
232 #endif
233 #define OP_GREEDY        (MAX_CHAR+1) // * + ? {n}
234 #define OP_OR            (MAX_CHAR+2)
235 #define OP_EXPR            (MAX_CHAR+3) //parentesis ()
236 #define OP_NOCAPEXPR    (MAX_CHAR+4) //parentesis (?:)
237 #define OP_DOT            (MAX_CHAR+5)
238 #define OP_CLASS        (MAX_CHAR+6)
239 #define OP_CCLASS        (MAX_CHAR+7)
240 #define OP_NCLASS        (MAX_CHAR+8) //negates class the [^
241 #define OP_RANGE        (MAX_CHAR+9)
242 #define OP_CHAR            (MAX_CHAR+10)
243 #define OP_EOL            (MAX_CHAR+11)
244 #define OP_BOL            (MAX_CHAR+12)
245 #define OP_WB            (MAX_CHAR+13)
247 #define TREX_SYMBOL_ANY_CHAR ('.')
248 #define TREX_SYMBOL_GREEDY_ONE_OR_MORE ('+')
249 #define TREX_SYMBOL_GREEDY_ZERO_OR_MORE ('*')
250 #define TREX_SYMBOL_GREEDY_ZERO_OR_ONE ('?')
251 #define TREX_SYMBOL_BRANCH ('|')
252 #define TREX_SYMBOL_END_OF_STRING ('$')
253 #define TREX_SYMBOL_BEGINNING_OF_STRING ('^')
254 #define TREX_SYMBOL_ESCAPE_CHAR ('\\')
257 typedef int TRexNodeType;
259 typedef struct tagTRexNode{
260     TRexNodeType type;
261     int left;
262     int right;
263     int next;
264 }TRexNode;
266 struct TRex{
267     const TRexChar *_eol;
268     const TRexChar *_bol;
269     const TRexChar *_p;
270     int _first;
271     int _op;
272     TRexNode *_nodes;
273     int _nallocated;
274     int _nsize;
275     int _nsubexpr;
276     TRexMatch *_matches;
277     int _currsubexp;
278     void *_jmpbuf;
279     const TRexChar **_error;
280 };
282 static int trex_list(TRex *exp);
284 static int trex_newnode(TRex *exp, TRexNodeType type)
286     TRexNode n;
287     int newid;
288     n.type = type;
289     n.next = n.right = n.left = -1;
290     if(type == OP_EXPR)
291         n.right = exp->_nsubexpr++;
292     if(exp->_nallocated < (exp->_nsize + 1)) {
293         //int oldsize = exp->_nallocated;
294         exp->_nallocated *= 2;
295         exp->_nodes = (TRexNode *)realloc(exp->_nodes, exp->_nallocated * sizeof(TRexNode));
296     }
297     exp->_nodes[exp->_nsize++] = n;
298     newid = exp->_nsize - 1;
299     return (int)newid;
302 static void trex_error(TRex *exp,const TRexChar *error)
304     if(exp->_error) *exp->_error = error;
305     longjmp(*((jmp_buf*)exp->_jmpbuf),-1);
308 static void trex_expect(TRex *exp, int n){
309     if((*exp->_p) != n) 
310         trex_error(exp, _SC("expected paren"));
311     exp->_p++;
314 static TRexChar trex_escapechar(TRex *exp)
316     if(*exp->_p == TREX_SYMBOL_ESCAPE_CHAR){
317         exp->_p++;
318         switch(*exp->_p) {
319         case 'v': exp->_p++; return '\v';
320         case 'n': exp->_p++; return '\n';
321         case 't': exp->_p++; return '\t';
322         case 'r': exp->_p++; return '\r';
323         case 'f': exp->_p++; return '\f';
324         default: return (*exp->_p++);
325         }
326     } else if(!scisprint(*exp->_p)) trex_error(exp,_SC("letter expected"));
327     return (*exp->_p++);
330 static int trex_charclass(TRex *exp,int classid)
332     int n = trex_newnode(exp,OP_CCLASS);
333     exp->_nodes[n].left = classid;
334     return n;
337 static int trex_charnode(TRex *exp,TRexBool isclass)
339     TRexChar t;
340     if(*exp->_p == TREX_SYMBOL_ESCAPE_CHAR) {
341         exp->_p++;
342         switch(*exp->_p) {
343             case 'n': exp->_p++; return trex_newnode(exp,'\n');
344             case 't': exp->_p++; return trex_newnode(exp,'\t');
345             case 'r': exp->_p++; return trex_newnode(exp,'\r');
346             case 'f': exp->_p++; return trex_newnode(exp,'\f');
347             case 'v': exp->_p++; return trex_newnode(exp,'\v');
348             case 'a': case 'A': case 'w': case 'W': case 's': case 'S': 
349             case 'd': case 'D': case 'x': case 'X': case 'c': case 'C': 
350             case 'p': case 'P': case 'l': case 'u': 
351                 {
352                 t = *exp->_p; exp->_p++; 
353                 return trex_charclass(exp,t);
354                 }
355             case 'b': 
356             case 'B':
357                 if(!isclass) {
358                     int node = trex_newnode(exp,OP_WB);
359                     exp->_nodes[node].left = *exp->_p;
360                     exp->_p++; 
361                     return node;
362                 } //else default
363             default: 
364                 t = *exp->_p; exp->_p++; 
365                 return trex_newnode(exp,t);
366         }
367     }
368     else if(!scisprint(*exp->_p)) {
369         
370         trex_error(exp,_SC("letter expected"));
371     }
372     t = *exp->_p; exp->_p++; 
373     return trex_newnode(exp,t);
375 static int trex_class(TRex *exp)
377     int ret = -1;
378     int first = -1,chain;
379     if(*exp->_p == TREX_SYMBOL_BEGINNING_OF_STRING){
380         ret = trex_newnode(exp,OP_NCLASS);
381         exp->_p++;
382     }else ret = trex_newnode(exp,OP_CLASS);
383     
384     if(*exp->_p == ']') trex_error(exp,_SC("empty class"));
385     chain = ret;
386     while(*exp->_p != ']' && exp->_p != exp->_eol) {
387         if(*exp->_p == '-' && first != -1){ 
388             int r,t;
389             if(*exp->_p++ == ']') trex_error(exp,_SC("unfinished range"));
390             r = trex_newnode(exp,OP_RANGE);
391             if(first>*exp->_p) trex_error(exp,_SC("invalid range"));
392             if(exp->_nodes[first].type == OP_CCLASS) trex_error(exp,_SC("cannot use character classes in ranges"));
393             exp->_nodes[r].left = exp->_nodes[first].type;
394             t = trex_escapechar(exp);
395             exp->_nodes[r].right = t;
396             exp->_nodes[chain].next = r;
397             chain = r;
398             first = -1;
399         }
400         else{
401             if(first!=-1){
402                 int c = first;
403                 exp->_nodes[chain].next = c;
404                 chain = c;
405                 first = trex_charnode(exp,TRex_True);
406             }
407             else{
408                 first = trex_charnode(exp,TRex_True);
409             }
410         }
411     }
412     if(first!=-1){
413         int c = first;
414         exp->_nodes[chain].next = c;
415         chain = c;
416         first = -1;
417     }
418     /* hack? */
419     exp->_nodes[ret].left = exp->_nodes[ret].next;
420     exp->_nodes[ret].next = -1;
421     return ret;
424 static int trex_parsenumber(TRex *exp)
426     int ret = *exp->_p-'0';
427     int positions = 10;
428     exp->_p++;
429     while(isdigit(*exp->_p)) {
430         ret = ret*10+(*exp->_p++-'0');
431         if(positions==1000000000) trex_error(exp,_SC("overflow in numeric constant"));
432         positions *= 10;
433     };
434     return ret;
437 static int trex_element(TRex *exp)
439     int ret = -1;
440     switch(*exp->_p)
441     {
442     case '(': {
443         int expr,newn;
444         exp->_p++;
447         if(*exp->_p =='?') {
448             exp->_p++;
449             trex_expect(exp,':');
450             expr = trex_newnode(exp,OP_NOCAPEXPR);
451         }
452         else
453             expr = trex_newnode(exp,OP_EXPR);
454         newn = trex_list(exp);
455         exp->_nodes[expr].left = newn;
456         ret = expr;
457         trex_expect(exp,')');
458               }
459               break;
460     case '[':
461         exp->_p++;
462         ret = trex_class(exp);
463         trex_expect(exp,']');
464         break;
465     case TREX_SYMBOL_END_OF_STRING: exp->_p++; ret = trex_newnode(exp,OP_EOL);break;
466     case TREX_SYMBOL_ANY_CHAR: exp->_p++; ret = trex_newnode(exp,OP_DOT);break;
467     default:
468         ret = trex_charnode(exp,TRex_False);
469         break;
470     }
472     {
473         int op;
474         TRexBool isgreedy = TRex_False;
475         unsigned short p0 = 0, p1 = 0;
476         switch(*exp->_p){
477             case TREX_SYMBOL_GREEDY_ZERO_OR_MORE: p0 = 0; p1 = 0xFFFF; exp->_p++; isgreedy = TRex_True; break;
478             case TREX_SYMBOL_GREEDY_ONE_OR_MORE: p0 = 1; p1 = 0xFFFF; exp->_p++; isgreedy = TRex_True; break;
479             case TREX_SYMBOL_GREEDY_ZERO_OR_ONE: p0 = 0; p1 = 1; exp->_p++; isgreedy = TRex_True; break;
480             case '{':
481                 exp->_p++;
482                 if(!isdigit(*exp->_p)) trex_error(exp,_SC("number expected"));
483                 p0 = (unsigned short)trex_parsenumber(exp);
484                 /*******************************/
485                 switch(*exp->_p) {
486             case '}':
487                 p1 = p0; exp->_p++;
488                 break;
489             case ',':
490                 exp->_p++;
491                 p1 = 0xFFFF;
492                 if(isdigit(*exp->_p)){
493                     p1 = (unsigned short)trex_parsenumber(exp);
494                 }
495                 trex_expect(exp,'}');
496                 break;
497             default:
498                 trex_error(exp,_SC(", or } expected"));
499         }
500         /*******************************/
501         isgreedy = TRex_True; 
502         break;
504         }
505         if(isgreedy) {
506             int nnode = trex_newnode(exp,OP_GREEDY);
507             op = OP_GREEDY;
508             exp->_nodes[nnode].left = ret;
509             exp->_nodes[nnode].right = ((p0)<<16)|p1;
510             ret = nnode;
511         }
512     }
513     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')) {
514         int nnode = trex_element(exp);
515         exp->_nodes[ret].next = nnode;
516     }
518     return ret;
521 static int trex_list(TRex *exp)
523     int ret=-1,e;
524     if(*exp->_p == TREX_SYMBOL_BEGINNING_OF_STRING) {
525         exp->_p++;
526         ret = trex_newnode(exp,OP_BOL);
527     }
528     e = trex_element(exp);
529     if(ret != -1) {
530         exp->_nodes[ret].next = e;
531     }
532     else ret = e;
534     if(*exp->_p == TREX_SYMBOL_BRANCH) {
535         int temp,tright;
536         exp->_p++;
537         temp = trex_newnode(exp,OP_OR);
538         exp->_nodes[temp].left = ret;
539         tright = trex_list(exp);
540         exp->_nodes[temp].right = tright;
541         ret = temp;
542     }
543     return ret;
546 static TRexBool trex_matchcclass(int cclass,TRexChar c)
548     switch(cclass) {
549     case 'a': return isalpha(c)?TRex_True:TRex_False;
550     case 'A': return !isalpha(c)?TRex_True:TRex_False;
551     case 'w': return (isalnum(c) || c == '_')?TRex_True:TRex_False;
552     case 'W': return (!isalnum(c) && c != '_')?TRex_True:TRex_False;
553     case 's': return isspace(c)?TRex_True:TRex_False;
554     case 'S': return !isspace(c)?TRex_True:TRex_False;
555     case 'd': return isdigit(c)?TRex_True:TRex_False;
556     case 'D': return !isdigit(c)?TRex_True:TRex_False;
557     case 'x': return isxdigit(c)?TRex_True:TRex_False;
558     case 'X': return !isxdigit(c)?TRex_True:TRex_False;
559     case 'c': return iscntrl(c)?TRex_True:TRex_False;
560     case 'C': return !iscntrl(c)?TRex_True:TRex_False;
561     case 'p': return ispunct(c)?TRex_True:TRex_False;
562     case 'P': return !ispunct(c)?TRex_True:TRex_False;
563     case 'l': return islower(c)?TRex_True:TRex_False;
564     case 'u': return isupper(c)?TRex_True:TRex_False;
565     }
566     return TRex_False; /*cannot happen*/
569 static TRexBool trex_matchclass(TRex* exp,TRexNode *node,TRexChar c)
571     do {
572         switch(node->type) {
573             case OP_RANGE:
574                 if(c >= node->left && c <= node->right) return TRex_True;
575                 break;
576             case OP_CCLASS:
577                 if(trex_matchcclass(node->left,c)) return TRex_True;
578                 break;
579             default:
580                 if(c == node->type)return TRex_True;
581         }
582     } while((node->next != -1) && (node = &exp->_nodes[node->next]));
583     return TRex_False;
586 static const TRexChar *trex_matchnode(TRex* exp,TRexNode *node,const TRexChar *str,TRexNode *next)
588     
589     TRexNodeType type = node->type;
590     switch(type) {
591     case OP_GREEDY: {
592         //TRexNode *greedystop = (node->next != -1) ? &exp->_nodes[node->next] : NULL;
593         TRexNode *greedystop = NULL;
594         int p0 = (node->right >> 16)&0x0000FFFF, p1 = node->right&0x0000FFFF, nmaches = 0;
595         const TRexChar *s=str, *good = str;
597         if(node->next != -1) {
598             greedystop = &exp->_nodes[node->next];
599         }
600         else {
601             greedystop = next;
602         }
604         while((nmaches == 0xFFFF || nmaches < p1)) {
606             const TRexChar *stop;
607             if(!(s = trex_matchnode(exp,&exp->_nodes[node->left],s,greedystop)))
608                 break;
609             nmaches++;
610             good=s;
611             if(greedystop) {
612                 //checks that 0 matches satisfy the expression(if so skips)
613                 //if not would always stop(for instance if is a '?')
614                 if(greedystop->type != OP_GREEDY ||
615                 (greedystop->type == OP_GREEDY && ((greedystop->right >> 16)&0x0000FFFF) != 0))
616                 {
617                     TRexNode *gnext = NULL;
618                     if(greedystop->next != -1) {
619                         gnext = &exp->_nodes[greedystop->next];
620                     }else if(next && next->next != -1){
621                         gnext = &exp->_nodes[next->next];
622                     }
623                     stop = trex_matchnode(exp,greedystop,s,gnext);
624                     if(stop) {
625                         //if satisfied stop it
626                         if(p0 == p1 && p0 == nmaches) break;
627                         else if(nmaches >= p0 && p1 == 0xFFFF) break;
628                         else if(nmaches >= p0 && nmaches <= p1) break;
629                     }
630                 }
631             }
632             
633             if(s >= exp->_eol)
634                 break;
635         }
636         if(p0 == p1 && p0 == nmaches) return good;
637         else if(nmaches >= p0 && p1 == 0xFFFF) return good;
638         else if(nmaches >= p0 && nmaches <= p1) return good;
639         return NULL;
640     }
641     case OP_OR: {
642             const TRexChar *asd = str;
643             TRexNode *temp=&exp->_nodes[node->left];
644             while( (asd = trex_matchnode(exp,temp,asd,NULL)) ) {
645                 if(temp->next != -1)
646                     temp = &exp->_nodes[temp->next];
647                 else
648                     return asd;
649             }
650             asd = str;
651             temp = &exp->_nodes[node->right];
652             while( (asd = trex_matchnode(exp,temp,asd,NULL)) ) {
653                 if(temp->next != -1)
654                     temp = &exp->_nodes[temp->next];
655                 else
656                     return asd;
657             }
658             return NULL;
659             break;
660     }
661     case OP_EXPR:
662     case OP_NOCAPEXPR:{
663             TRexNode *n = &exp->_nodes[node->left];
664             const TRexChar *cur = str;
665             int capture = -1;
666             if(node->type != OP_NOCAPEXPR && node->right == exp->_currsubexp) {
667                 capture = exp->_currsubexp;
668                 exp->_matches[capture].begin = cur;
669                 exp->_currsubexp++;
670             }
671             
672             do {
673                 TRexNode *subnext = NULL;
674                 if(n->next != -1) {
675                     subnext = &exp->_nodes[n->next];
676                 }else {
677                     subnext = next;
678                 }
679                 if(!(cur = trex_matchnode(exp,n,cur,subnext))) {
680                     if(capture != -1){
681                         exp->_matches[capture].begin = 0;
682                         exp->_matches[capture].len = 0;
683                     }
684                     return NULL;
685                 }
686             } while((n->next != -1) && (n = &exp->_nodes[n->next]));
688             if(capture != -1) 
689                 exp->_matches[capture].len = cur - exp->_matches[capture].begin;
690             return cur;
691     }                 
692     case OP_WB:
693         if(str == exp->_bol && !isspace(*str)
694          || (str == exp->_eol && !isspace(*(str-1)))
695          || (!isspace(*str) && isspace(*(str+1)))
696          || (isspace(*str) && !isspace(*(str+1))) ) {
697             return (node->left == 'b')?str:NULL;
698         }
699         return (node->left == 'b')?NULL:str;
700     case OP_BOL:
701         if(str == exp->_bol) return str;
702         return NULL;
703     case OP_EOL:
704         if(str == exp->_eol) return str;
705         return NULL;
706     case OP_DOT:{
707         *str++;
708                 }
709         return str;
710     case OP_NCLASS:
711     case OP_CLASS:
712         if(trex_matchclass(exp,&exp->_nodes[node->left],*str)?(type == OP_CLASS?TRex_True:TRex_False):(type == OP_NCLASS?TRex_True:TRex_False)) {
713             *str++;
714             return str;
715         }
716         return NULL;
717     case OP_CCLASS:
718         if(trex_matchcclass(node->left,*str)) {
719             *str++;
720             return str;
721         }
722         return NULL;
723     default: /* char */
724         if(*str != node->type) return NULL;
725         *str++;
726         return str;
727     }
728     return NULL;
731 /* public api */
732 TRex *trex_compile(const TRexChar *pattern,const TRexChar **error)
734     TRex *exp = (TRex *)malloc(sizeof(TRex));
735     exp->_eol = exp->_bol = NULL;
736     exp->_p = pattern;
737     exp->_nallocated = (int)scstrlen(pattern) * sizeof(TRexChar);
738     exp->_nodes = (TRexNode *)malloc(exp->_nallocated * sizeof(TRexNode));
739     exp->_nsize = 0;
740     exp->_matches = 0;
741     exp->_nsubexpr = 0;
742     exp->_first = trex_newnode(exp,OP_EXPR);
743     exp->_error = error;
744     exp->_jmpbuf = malloc(sizeof(jmp_buf));
745     if(setjmp(*((jmp_buf*)exp->_jmpbuf)) == 0) {
746         int res = trex_list(exp);
747         exp->_nodes[exp->_first].left = res;
748         if(*exp->_p!='\0')
749             trex_error(exp,_SC("unexpected character"));
750 #ifdef _DEBUG
751         {
752             int nsize,i;
753             TRexNode *t;
754             nsize = exp->_nsize;
755             t = &exp->_nodes[0];
756             scprintf(_SC("\n"));
757             for(i = 0;i < nsize; i++) {
758                 if(exp->_nodes[i].type>MAX_CHAR)
759                     scprintf(_SC("[%02d] %10s "),i,g_nnames[exp->_nodes[i].type-MAX_CHAR]);
760                 else
761                     scprintf(_SC("[%02d] %10c "),i,exp->_nodes[i].type);
762                 scprintf(_SC("left %02d right %02d next %02d\n"),exp->_nodes[i].left,exp->_nodes[i].right,exp->_nodes[i].next);
763             }
764             scprintf(_SC("\n"));
765         }
766 #endif
767         exp->_matches = (TRexMatch *) malloc(exp->_nsubexpr * sizeof(TRexMatch));
768         memset(exp->_matches,0,exp->_nsubexpr * sizeof(TRexMatch));
769     }
770     else{
771         trex_free(exp);
772         return NULL;
773     }
774     return exp;
777 void trex_free(TRex *exp)
779     if(exp)    {
780         if(exp->_nodes) free(exp->_nodes);
781         if(exp->_jmpbuf) free(exp->_jmpbuf);
782         if(exp->_matches) free(exp->_matches);
783         free(exp);
784     }
787 TRexBool trex_match(TRex* exp,const TRexChar* text)
789     const TRexChar* res = NULL;
790     exp->_bol = text;
791     exp->_eol = text + scstrlen(text);
792     exp->_currsubexp = 0;
793     res = trex_matchnode(exp,exp->_nodes,text,NULL);
794     if(res == NULL || res != exp->_eol)
795         return TRex_False;
796     return TRex_True;
799 TRexBool trex_searchrange(TRex* exp,const TRexChar* text_begin,const TRexChar* text_end,const TRexChar** out_begin, const TRexChar** out_end)
801     const TRexChar *cur = NULL;
802     int node = exp->_first;
803     if(text_begin >= text_end) return TRex_False;
804     exp->_bol = text_begin;
805     exp->_eol = text_end;
806     do {
807         cur = text_begin;
808         while(node != -1) {
809             exp->_currsubexp = 0;
810             cur = trex_matchnode(exp,&exp->_nodes[node],cur,NULL);
811             if(!cur)
812                 break;
813             node = exp->_nodes[node].next;
814         }
815         *text_begin++;
816     } while(cur == NULL && text_begin != text_end);
818     if(cur == NULL)
819         return TRex_False;
821     --text_begin;
823     if(out_begin) *out_begin = text_begin;
824     if(out_end) *out_end = cur;
825     return TRex_True;
828 TRexBool trex_search(TRex* exp,const TRexChar* text, const TRexChar** out_begin, const TRexChar** out_end)
830     return trex_searchrange(exp,text,text + scstrlen(text),out_begin,out_end);
833 int trex_getsubexpcount(TRex* exp)
835     return exp->_nsubexpr;
838 TRexBool trex_getsubexp(TRex* exp, int n, TRexMatch *subexp)
840     if( n<0 || n >= exp->_nsubexpr) return TRex_False;
841     *subexp = exp->_matches[n];
842     return TRex_True;
846 //########################################################################
847 //########################################################################
848 //##  E N D    R E G E X P
849 //########################################################################
850 //########################################################################
856 //########################################################################
857 //########################################################################
858 //##  X M L
859 //########################################################################
860 //########################################################################
862 // Note:  This mini-dom library comes from Pedro, another little project
863 // of mine.
865 typedef std::string String;
866 typedef unsigned int XMLCh;
869 class Namespace
871 public:
872     Namespace()
873         {}
875     Namespace(const String &prefixArg, const String &namespaceURIArg)
876         {
877         prefix       = prefixArg;
878         namespaceURI = namespaceURIArg;
879         }
881     Namespace(const Namespace &other)
882         {
883         assign(other);
884         }
886     Namespace &operator=(const Namespace &other)
887         {
888         assign(other);
889         return *this;
890         }
892     virtual ~Namespace()
893         {}
895     virtual String getPrefix()
896         { return prefix; }
898     virtual String getNamespaceURI()
899         { return namespaceURI; }
901 protected:
903     void assign(const Namespace &other)
904         {
905         prefix       = other.prefix;
906         namespaceURI = other.namespaceURI;
907         }
909     String prefix;
910     String namespaceURI;
912 };
914 class Attribute
916 public:
917     Attribute()
918         {}
920     Attribute(const String &nameArg, const String &valueArg)
921         {
922         name  = nameArg;
923         value = valueArg;
924         }
926     Attribute(const Attribute &other)
927         {
928         assign(other);
929         }
931     Attribute &operator=(const Attribute &other)
932         {
933         assign(other);
934         return *this;
935         }
937     virtual ~Attribute()
938         {}
940     virtual String getName()
941         { return name; }
943     virtual String getValue()
944         { return value; }
946 protected:
948     void assign(const Attribute &other)
949         {
950         name  = other.name;
951         value = other.value;
952         }
954     String name;
955     String value;
957 };
960 class Element
962 friend class Parser;
964 public:
965     Element()
966         {
967         init();
968         }
970     Element(const String &nameArg)
971         {
972         init();
973         name   = nameArg;
974         }
976     Element(const String &nameArg, const String &valueArg)
977         {
978         init();
979         name   = nameArg;
980         value  = valueArg;
981         }
983     Element(const Element &other)
984         {
985         assign(other);
986         }
988     Element &operator=(const Element &other)
989         {
990         assign(other);
991         return *this;
992         }
994     virtual Element *clone();
996     virtual ~Element()
997         {
998         for (unsigned int i=0 ; i<children.size() ; i++)
999             delete children[i];
1000         }
1002     virtual String getName()
1003         { return name; }
1005     virtual String getValue()
1006         { return value; }
1008     Element *getParent()
1009         { return parent; }
1011     std::vector<Element *> getChildren()
1012         { return children; }
1014     std::vector<Element *> findElements(const String &name);
1016     String getAttribute(const String &name);
1018     std::vector<Attribute> &getAttributes()
1019         { return attributes; } 
1021     String getTagAttribute(const String &tagName, const String &attrName);
1023     String getTagValue(const String &tagName);
1025     void addChild(Element *child);
1027     void addAttribute(const String &name, const String &value);
1029     void addNamespace(const String &prefix, const String &namespaceURI);
1032     /**
1033      * Prettyprint an XML tree to an output stream.  Elements are indented
1034      * according to element hierarchy.
1035      * @param f a stream to receive the output
1036      * @param elem the element to output
1037      */
1038     void writeIndented(FILE *f);
1040     /**
1041      * Prettyprint an XML tree to standard output.  This is the equivalent of
1042      * writeIndented(stdout).
1043      * @param elem the element to output
1044      */
1045     void print();
1046     
1047     int getLine()
1048         { return line; }
1050 protected:
1052     void init()
1053         {
1054         parent = NULL;
1055         line   = 0;
1056         }
1058     void assign(const Element &other)
1059         {
1060         parent     = other.parent;
1061         children   = other.children;
1062         attributes = other.attributes;
1063         namespaces = other.namespaces;
1064         name       = other.name;
1065         value      = other.value;
1066         line       = other.line;
1067         }
1069     void findElementsRecursive(std::vector<Element *>&res, const String &name);
1071     void writeIndentedRecursive(FILE *f, int indent);
1073     Element *parent;
1075     std::vector<Element *>children;
1077     std::vector<Attribute> attributes;
1078     std::vector<Namespace> namespaces;
1080     String name;
1081     String value;
1082     
1083     int line;
1084 };
1090 class Parser
1092 public:
1093     /**
1094      * Constructor
1095      */
1096     Parser()
1097         { init(); }
1099     virtual ~Parser()
1100         {}
1102     /**
1103      * Parse XML in a char buffer.
1104      * @param buf a character buffer to parse
1105      * @param pos position to start parsing
1106      * @param len number of chars, from pos, to parse.
1107      * @return a pointer to the root of the XML document;
1108      */
1109     Element *parse(const char *buf,int pos,int len);
1111     /**
1112      * Parse XML in a char buffer.
1113      * @param buf a character buffer to parse
1114      * @param pos position to start parsing
1115      * @param len number of chars, from pos, to parse.
1116      * @return a pointer to the root of the XML document;
1117      */
1118     Element *parse(const String &buf);
1120     /**
1121      * Parse a named XML file.  The file is loaded like a data file;
1122      * the original format is not preserved.
1123      * @param fileName the name of the file to read
1124      * @return a pointer to the root of the XML document;
1125      */
1126     Element *parseFile(const String &fileName);
1128     /**
1129      * Utility method to preprocess a string for XML
1130      * output, escaping its entities.
1131      * @param str the string to encode
1132      */
1133     static String encode(const String &str);
1135     /**
1136      *  Removes whitespace from beginning and end of a string
1137      */
1138     String trim(const String &s);
1140 private:
1142     void init()
1143         {
1144         keepGoing       = true;
1145         currentNode     = NULL;
1146         parselen        = 0;
1147         parsebuf        = NULL;
1148         currentPosition = 0;
1149         }
1151     int countLines(int begin, int end);
1153     void getLineAndColumn(int pos, int *lineNr, int *colNr);
1155     void error(const char *fmt, ...);
1157     int peek(int pos);
1159     int match(int pos, const char *text);
1161     int skipwhite(int p);
1163     int getWord(int p0, String &buf);
1165     int getQuoted(int p0, String &buf, int do_i_parse);
1167     int parseVersion(int p0);
1169     int parseDoctype(int p0);
1171     int parseElement(int p0, Element *par,int depth);
1173     Element *parse(XMLCh *buf,int pos,int len);
1175     bool       keepGoing;
1176     Element    *currentNode;
1177     int        parselen;
1178     XMLCh      *parsebuf;
1179     String     cdatabuf;
1180     int        currentPosition;
1181 };
1186 //########################################################################
1187 //# E L E M E N T
1188 //########################################################################
1190 Element *Element::clone()
1192     Element *elem = new Element(name, value);
1193     elem->parent     = parent;
1194     elem->attributes = attributes;
1195     elem->namespaces = namespaces;
1196     elem->line       = line;
1198     std::vector<Element *>::iterator iter;
1199     for (iter = children.begin(); iter != children.end() ; iter++)
1200         {
1201         elem->addChild((*iter)->clone());
1202         }
1203     return elem;
1207 void Element::findElementsRecursive(std::vector<Element *>&res, const String &name)
1209     if (getName() == name)
1210         {
1211         res.push_back(this);
1212         }
1213     for (unsigned int i=0; i<children.size() ; i++)
1214         children[i]->findElementsRecursive(res, name);
1217 std::vector<Element *> Element::findElements(const String &name)
1219     std::vector<Element *> res;
1220     findElementsRecursive(res, name);
1221     return res;
1224 String Element::getAttribute(const String &name)
1226     for (unsigned int i=0 ; i<attributes.size() ; i++)
1227         if (attributes[i].getName() ==name)
1228             return attributes[i].getValue();
1229     return "";
1232 String Element::getTagAttribute(const String &tagName, const String &attrName)
1234     std::vector<Element *>elems = findElements(tagName);
1235     if (elems.size() <1)
1236         return "";
1237     String res = elems[0]->getAttribute(attrName);
1238     return res;
1241 String Element::getTagValue(const String &tagName)
1243     std::vector<Element *>elems = findElements(tagName);
1244     if (elems.size() <1)
1245         return "";
1246     String res = elems[0]->getValue();
1247     return res;
1250 void Element::addChild(Element *child)
1252     if (!child)
1253         return;
1254     child->parent = this;
1255     children.push_back(child);
1259 void Element::addAttribute(const String &name, const String &value)
1261     Attribute attr(name, value);
1262     attributes.push_back(attr);
1265 void Element::addNamespace(const String &prefix, const String &namespaceURI)
1267     Namespace ns(prefix, namespaceURI);
1268     namespaces.push_back(ns);
1271 void Element::writeIndentedRecursive(FILE *f, int indent)
1273     int i;
1274     if (!f)
1275         return;
1276     //Opening tag, and attributes
1277     for (i=0;i<indent;i++)
1278         fputc(' ',f);
1279     fprintf(f,"<%s",name.c_str());
1280     for (unsigned int i=0 ; i<attributes.size() ; i++)
1281         {
1282         fprintf(f," %s=\"%s\"",
1283               attributes[i].getName().c_str(),
1284               attributes[i].getValue().c_str());
1285         }
1286     for (unsigned int i=0 ; i<namespaces.size() ; i++)
1287         {
1288         fprintf(f," xmlns:%s=\"%s\"",
1289               namespaces[i].getPrefix().c_str(),
1290               namespaces[i].getNamespaceURI().c_str());
1291         }
1292     fprintf(f,">\n");
1294     //Between the tags
1295     if (value.size() > 0)
1296         {
1297         for (int i=0;i<indent;i++)
1298             fputc(' ', f);
1299         fprintf(f," %s\n", value.c_str());
1300         }
1302     for (unsigned int i=0 ; i<children.size() ; i++)
1303         children[i]->writeIndentedRecursive(f, indent+2);
1305     //Closing tag
1306     for (int i=0; i<indent; i++)
1307         fputc(' ',f);
1308     fprintf(f,"</%s>\n", name.c_str());
1311 void Element::writeIndented(FILE *f)
1313     writeIndentedRecursive(f, 0);
1316 void Element::print()
1318     writeIndented(stdout);
1322 //########################################################################
1323 //# P A R S E R
1324 //########################################################################
1328 typedef struct
1329     {
1330     const char *escaped;
1331     char value;
1332     } EntityEntry;
1334 static EntityEntry entities[] =
1336     { "&amp;" , '&'  },
1337     { "&lt;"  , '<'  },
1338     { "&gt;"  , '>'  },
1339     { "&apos;", '\'' },
1340     { "&quot;", '"'  },
1341     { NULL    , '\0' }
1342 };
1346 /**
1347  *  Removes whitespace from beginning and end of a string
1348  */
1349 String Parser::trim(const String &s)
1351     if (s.size() < 1)
1352         return s;
1353     
1354     //Find first non-ws char
1355     unsigned int begin = 0;
1356     for ( ; begin < s.size() ; begin++)
1357         {
1358         if (!isspace(s[begin]))
1359             break;
1360         }
1362     //Find first non-ws char, going in reverse
1363     unsigned int end = s.size() - 1;
1364     for ( ; end > begin ; end--)
1365         {
1366         if (!isspace(s[end]))
1367             break;
1368         }
1369     //trace("begin:%d  end:%d", begin, end);
1371     String res = s.substr(begin, end-begin+1);
1372     return res;
1376 int Parser::countLines(int begin, int end)
1378     int count = 0;
1379     for (int i=begin ; i<end ; i++)
1380         {
1381         XMLCh ch = parsebuf[i];
1382         if (ch == '\n' || ch == '\r')
1383             count++;
1384         }
1385     return count;
1389 void Parser::getLineAndColumn(int pos, int *lineNr, int *colNr)
1391     int line = 1;
1392     int col  = 1;
1393     for (long i=0 ; i<pos ; i++)
1394         {
1395         XMLCh ch = parsebuf[i];
1396         if (ch == '\n' || ch == '\r')
1397             {
1398             col = 0;
1399             line ++;
1400             }
1401         else
1402             col++;
1403         }
1404     *lineNr = line;
1405     *colNr  = col;
1410 void Parser::error(const char *fmt, ...)
1412     int lineNr;
1413     int colNr;
1414     getLineAndColumn(currentPosition, &lineNr, &colNr);
1415     va_list args;
1416     fprintf(stderr, "xml error at line %d, column %d:", lineNr, colNr);
1417     va_start(args,fmt);
1418     vfprintf(stderr,fmt,args);
1419     va_end(args) ;
1420     fprintf(stderr, "\n");
1425 int Parser::peek(int pos)
1427     if (pos >= parselen)
1428         return -1;
1429     currentPosition = pos;
1430     int ch = parsebuf[pos];
1431     //printf("ch:%c\n", ch);
1432     return ch;
1437 String Parser::encode(const String &str)
1439     String ret;
1440     for (unsigned int i=0 ; i<str.size() ; i++)
1441         {
1442         XMLCh ch = (XMLCh)str[i];
1443         if (ch == '&')
1444             ret.append("&amp;");
1445         else if (ch == '<')
1446             ret.append("&lt;");
1447         else if (ch == '>')
1448             ret.append("&gt;");
1449         else if (ch == '\'')
1450             ret.append("&apos;");
1451         else if (ch == '"')
1452             ret.append("&quot;");
1453         else
1454             ret.push_back(ch);
1456         }
1457     return ret;
1461 int Parser::match(int p0, const char *text)
1463     int p = p0;
1464     while (*text)
1465         {
1466         if (peek(p) != *text)
1467             return p0;
1468         p++; text++;
1469         }
1470     return p;
1475 int Parser::skipwhite(int p)
1478     while (p<parselen)
1479         {
1480         int p2 = match(p, "<!--");
1481         if (p2 > p)
1482             {
1483             p = p2;
1484             while (p<parselen)
1485               {
1486               p2 = match(p, "-->");
1487               if (p2 > p)
1488                   {
1489                   p = p2;
1490                   break;
1491                   }
1492               p++;
1493               }
1494           }
1495       XMLCh b = peek(p);
1496       if (!isspace(b))
1497           break;
1498       p++;
1499       }
1500   return p;
1503 /* modify this to allow all chars for an element or attribute name*/
1504 int Parser::getWord(int p0, String &buf)
1506     int p = p0;
1507     while (p<parselen)
1508         {
1509         XMLCh b = peek(p);
1510         if (b<=' ' || b=='/' || b=='>' || b=='=')
1511             break;
1512         buf.push_back(b);
1513         p++;
1514         }
1515     return p;
1518 int Parser::getQuoted(int p0, String &buf, int do_i_parse)
1521     int p = p0;
1522     if (peek(p) != '"' && peek(p) != '\'')
1523         return p0;
1524     p++;
1526     while ( p<parselen )
1527         {
1528         XMLCh b = peek(p);
1529         if (b=='"' || b=='\'')
1530             break;
1531         if (b=='&' && do_i_parse)
1532             {
1533             bool found = false;
1534             for (EntityEntry *ee = entities ; ee->value ; ee++)
1535                 {
1536                 int p2 = match(p, ee->escaped);
1537                 if (p2>p)
1538                     {
1539                     buf.push_back(ee->value);
1540                     p = p2;
1541                     found = true;
1542                     break;
1543                     }
1544                 }
1545             if (!found)
1546                 {
1547                 error("unterminated entity");
1548                 return false;
1549                 }
1550             }
1551         else
1552             {
1553             buf.push_back(b);
1554             p++;
1555             }
1556         }
1557     return p;
1560 int Parser::parseVersion(int p0)
1562     //printf("### parseVersion: %d\n", p0);
1564     int p = p0;
1566     p = skipwhite(p0);
1568     if (peek(p) != '<')
1569         return p0;
1571     p++;
1572     if (p>=parselen || peek(p)!='?')
1573         return p0;
1575     p++;
1577     String buf;
1579     while (p<parselen)
1580         {
1581         XMLCh ch = peek(p);
1582         if (ch=='?')
1583             {
1584             p++;
1585             break;
1586             }
1587         buf.push_back(ch);
1588         p++;
1589         }
1591     if (peek(p) != '>')
1592         return p0;
1593     p++;
1595     //printf("Got version:%s\n",buf.c_str());
1596     return p;
1599 int Parser::parseDoctype(int p0)
1601     //printf("### parseDoctype: %d\n", p0);
1603     int p = p0;
1604     p = skipwhite(p);
1606     if (p>=parselen || peek(p)!='<')
1607         return p0;
1609     p++;
1611     if (peek(p)!='!' || peek(p+1)=='-')
1612         return p0;
1613     p++;
1615     String buf;
1616     while (p<parselen)
1617         {
1618         XMLCh ch = peek(p);
1619         if (ch=='>')
1620             {
1621             p++;
1622             break;
1623             }
1624         buf.push_back(ch);
1625         p++;
1626         }
1628     //printf("Got doctype:%s\n",buf.c_str());
1629     return p;
1634 int Parser::parseElement(int p0, Element *par,int lineNr)
1637     int p = p0;
1639     int p2 = p;
1641     p = skipwhite(p);
1643     //## Get open tag
1644     XMLCh ch = peek(p);
1645     if (ch!='<')
1646         return p0;
1648     //int line, col;
1649     //getLineAndColumn(p, &line, &col);
1651     p++;
1653     String openTagName;
1654     p = skipwhite(p);
1655     p = getWord(p, openTagName);
1656     //printf("####tag :%s\n", openTagName.c_str());
1657     p = skipwhite(p);
1659     //Add element to tree
1660     Element *n = new Element(openTagName);
1661     n->line = lineNr + countLines(p0, p);
1662     n->parent = par;
1663     par->addChild(n);
1665     // Get attributes
1666     if (peek(p) != '>')
1667         {
1668         while (p<parselen)
1669             {
1670             p = skipwhite(p);
1671             ch = peek(p);
1672             //printf("ch:%c\n",ch);
1673             if (ch=='>')
1674                 break;
1675             else if (ch=='/' && p<parselen+1)
1676                 {
1677                 p++;
1678                 p = skipwhite(p);
1679                 ch = peek(p);
1680                 if (ch=='>')
1681                     {
1682                     p++;
1683                     //printf("quick close\n");
1684                     return p;
1685                     }
1686                 }
1687             String attrName;
1688             p2 = getWord(p, attrName);
1689             if (p2==p)
1690                 break;
1691             //printf("name:%s",buf);
1692             p=p2;
1693             p = skipwhite(p);
1694             ch = peek(p);
1695             //printf("ch:%c\n",ch);
1696             if (ch!='=')
1697                 break;
1698             p++;
1699             p = skipwhite(p);
1700             // ch = parsebuf[p];
1701             // printf("ch:%c\n",ch);
1702             String attrVal;
1703             p2 = getQuoted(p, attrVal, true);
1704             p=p2+1;
1705             //printf("name:'%s'   value:'%s'\n",attrName.c_str(),attrVal.c_str());
1706             char *namestr = (char *)attrName.c_str();
1707             if (strncmp(namestr, "xmlns:", 6)==0)
1708                 n->addNamespace(attrName, attrVal);
1709             else
1710                 n->addAttribute(attrName, attrVal);
1711             }
1712         }
1714     bool cdata = false;
1716     p++;
1717     // ### Get intervening data ### */
1718     String data;
1719     while (p<parselen)
1720         {
1721         //# COMMENT
1722         p2 = match(p, "<!--");
1723         if (!cdata && p2>p)
1724             {
1725             p = p2;
1726             while (p<parselen)
1727                 {
1728                 p2 = match(p, "-->");
1729                 if (p2 > p)
1730                     {
1731                     p = p2;
1732                     break;
1733                     }
1734                 p++;
1735                 }
1736             }
1738         ch = peek(p);
1739         //# END TAG
1740         if (ch=='<' && !cdata && peek(p+1)=='/')
1741             {
1742             break;
1743             }
1744         //# CDATA
1745         p2 = match(p, "<![CDATA[");
1746         if (p2 > p)
1747             {
1748             cdata = true;
1749             p = p2;
1750             continue;
1751             }
1753         //# CHILD ELEMENT
1754         if (ch == '<')
1755             {
1756             p2 = parseElement(p, n, lineNr + countLines(p0, p));
1757             if (p2 == p)
1758                 {
1759                 /*
1760                 printf("problem on element:%s.  p2:%d p:%d\n",
1761                       openTagName.c_str(), p2, p);
1762                 */
1763                 return p0;
1764                 }
1765             p = p2;
1766             continue;
1767             }
1768         //# ENTITY
1769         if (ch=='&' && !cdata)
1770             {
1771             bool found = false;
1772             for (EntityEntry *ee = entities ; ee->value ; ee++)
1773                 {
1774                 int p2 = match(p, ee->escaped);
1775                 if (p2>p)
1776                     {
1777                     data.push_back(ee->value);
1778                     p = p2;
1779                     found = true;
1780                     break;
1781                     }
1782                 }
1783             if (!found)
1784                 {
1785                 error("unterminated entity");
1786                 return -1;
1787                 }
1788             continue;
1789             }
1791         //# NONE OF THE ABOVE
1792         data.push_back(ch);
1793         p++;
1794         }/*while*/
1797     n->value = data;
1798     //printf("%d : data:%s\n",p,data.c_str());
1800     //## Get close tag
1801     p = skipwhite(p);
1802     ch = peek(p);
1803     if (ch != '<')
1804         {
1805         error("no < for end tag\n");
1806         return p0;
1807         }
1808     p++;
1809     ch = peek(p);
1810     if (ch != '/')
1811         {
1812         error("no / on end tag");
1813         return p0;
1814         }
1815     p++;
1816     ch = peek(p);
1817     p = skipwhite(p);
1818     String closeTagName;
1819     p = getWord(p, closeTagName);
1820     if (openTagName != closeTagName)
1821         {
1822         error("Mismatched closing tag.  Expected </%S>. Got '%S'.",
1823                 openTagName.c_str(), closeTagName.c_str());
1824         return p0;
1825         }
1826     p = skipwhite(p);
1827     if (peek(p) != '>')
1828         {
1829         error("no > on end tag for '%s'", closeTagName.c_str());
1830         return p0;
1831         }
1832     p++;
1833     // printf("close element:%s\n",closeTagName.c_str());
1834     p = skipwhite(p);
1835     return p;
1841 Element *Parser::parse(XMLCh *buf,int pos,int len)
1843     parselen = len;
1844     parsebuf = buf;
1845     Element *rootNode = new Element("root");
1846     pos = parseVersion(pos);
1847     pos = parseDoctype(pos);
1848     pos = parseElement(pos, rootNode, 1);
1849     return rootNode;
1853 Element *Parser::parse(const char *buf, int pos, int len)
1855     XMLCh *charbuf = new XMLCh[len + 1];
1856     long i = 0;
1857     for ( ; i < len ; i++)
1858         charbuf[i] = (XMLCh)buf[i];
1859     charbuf[i] = '\0';
1861     Element *n = parse(charbuf, pos, len);
1862     delete[] charbuf;
1863     return n;
1866 Element *Parser::parse(const String &buf)
1868     long len = (long)buf.size();
1869     XMLCh *charbuf = new XMLCh[len + 1];
1870     long i = 0;
1871     for ( ; i < len ; i++)
1872         charbuf[i] = (XMLCh)buf[i];
1873     charbuf[i] = '\0';
1875     Element *n = parse(charbuf, 0, len);
1876     delete[] charbuf;
1877     return n;
1880 Element *Parser::parseFile(const String &fileName)
1883     //##### LOAD INTO A CHAR BUF, THEN CONVERT TO XMLCh
1884     FILE *f = fopen(fileName.c_str(), "rb");
1885     if (!f)
1886         return NULL;
1888     struct stat  statBuf;
1889     if (fstat(fileno(f),&statBuf)<0)
1890         {
1891         fclose(f);
1892         return NULL;
1893         }
1894     long filelen = statBuf.st_size;
1896     //printf("length:%d\n",filelen);
1897     XMLCh *charbuf = new XMLCh[filelen + 1];
1898     for (XMLCh *p=charbuf ; !feof(f) ; p++)
1899         {
1900         *p = (XMLCh)fgetc(f);
1901         }
1902     fclose(f);
1903     charbuf[filelen] = '\0';
1906     /*
1907     printf("nrbytes:%d\n",wc_count);
1908     printf("buf:%ls\n======\n",charbuf);
1909     */
1910     Element *n = parse(charbuf, 0, filelen);
1911     delete[] charbuf;
1912     return n;
1915 //########################################################################
1916 //########################################################################
1917 //##  E N D    X M L
1918 //########################################################################
1919 //########################################################################
1926 //########################################################################
1927 //########################################################################
1928 //##  U R I
1929 //########################################################################
1930 //########################################################################
1932 //This would normally be a call to a UNICODE function
1933 #define isLetter(x) isalpha(x)
1935 /**
1936  *  A class that implements the W3C URI resource reference.
1937  */
1938 class URI
1940 public:
1942     typedef enum
1943         {
1944         SCHEME_NONE =0,
1945         SCHEME_DATA,
1946         SCHEME_HTTP,
1947         SCHEME_HTTPS,
1948         SCHEME_FTP,
1949         SCHEME_FILE,
1950         SCHEME_LDAP,
1951         SCHEME_MAILTO,
1952         SCHEME_NEWS,
1953         SCHEME_TELNET
1954         } SchemeTypes;
1956     /**
1957      *
1958      */
1959     URI()
1960         {
1961         init();
1962         }
1964     /**
1965      *
1966      */
1967     URI(const String &str)
1968         {
1969         init();
1970         parse(str);
1971         }
1974     /**
1975      *
1976      */
1977     URI(const char *str)
1978         {
1979         init();
1980         String domStr = str;
1981         parse(domStr);
1982         }
1985     /**
1986      *
1987      */
1988     URI(const URI &other)
1989         {
1990         init();
1991         assign(other);
1992         }
1995     /**
1996      *
1997      */
1998     URI &operator=(const URI &other)
1999         {
2000         init();
2001         assign(other);
2002         return *this;
2003         }
2006     /**
2007      *
2008      */
2009     virtual ~URI()
2010         {}
2014     /**
2015      *
2016      */
2017     virtual bool parse(const String &str);
2019     /**
2020      *
2021      */
2022     virtual String toString() const;
2024     /**
2025      *
2026      */
2027     virtual int getScheme() const;
2029     /**
2030      *
2031      */
2032     virtual String getSchemeStr() const;
2034     /**
2035      *
2036      */
2037     virtual String getAuthority() const;
2039     /**
2040      *  Same as getAuthority, but if the port has been specified
2041      *  as host:port , the port will not be included
2042      */
2043     virtual String getHost() const;
2045     /**
2046      *
2047      */
2048     virtual int getPort() const;
2050     /**
2051      *
2052      */
2053     virtual String getPath() const;
2055     /**
2056      *
2057      */
2058     virtual String getNativePath() const;
2060     /**
2061      *
2062      */
2063     virtual bool isAbsolute() const;
2065     /**
2066      *
2067      */
2068     virtual bool isOpaque() const;
2070     /**
2071      *
2072      */
2073     virtual String getQuery() const;
2075     /**
2076      *
2077      */
2078     virtual String getFragment() const;
2080     /**
2081      *
2082      */
2083     virtual URI resolve(const URI &other) const;
2085     /**
2086      *
2087      */
2088     virtual void normalize();
2090 private:
2092     /**
2093      *
2094      */
2095     void init()
2096         {
2097         parsebuf  = NULL;
2098         parselen  = 0;
2099         scheme    = SCHEME_NONE;
2100         schemeStr = "";
2101         port      = 0;
2102         authority = "";
2103         path      = "";
2104         absolute  = false;
2105         opaque    = false;
2106         query     = "";
2107         fragment  = "";
2108         }
2111     /**
2112      *
2113      */
2114     void assign(const URI &other)
2115         {
2116         scheme    = other.scheme;
2117         schemeStr = other.schemeStr;
2118         authority = other.authority;
2119         port      = other.port;
2120         path      = other.path;
2121         absolute  = other.absolute;
2122         opaque    = other.opaque;
2123         query     = other.query;
2124         fragment  = other.fragment;
2125         }
2127     int scheme;
2129     String schemeStr;
2131     String authority;
2133     bool portSpecified;
2135     int port;
2137     String path;
2139     bool absolute;
2141     bool opaque;
2143     String query;
2145     String fragment;
2147     void error(const char *fmt, ...);
2149     void trace(const char *fmt, ...);
2152     int peek(int p);
2154     int match(int p, const char *key);
2156     int parseScheme(int p);
2158     int parseHierarchicalPart(int p0);
2160     int parseQuery(int p0);
2162     int parseFragment(int p0);
2164     int parse(int p);
2166     char *parsebuf;
2168     int parselen;
2170 };
2174 typedef struct
2176     int         ival;
2177     const char *sval;
2178     int         port;
2179 } LookupEntry;
2181 LookupEntry schemes[] =
2183     { URI::SCHEME_DATA,   "data:",    0 },
2184     { URI::SCHEME_HTTP,   "http:",   80 },
2185     { URI::SCHEME_HTTPS,  "https:", 443 },
2186     { URI::SCHEME_FTP,    "ftp",     12 },
2187     { URI::SCHEME_FILE,   "file:",    0 },
2188     { URI::SCHEME_LDAP,   "ldap:",  123 },
2189     { URI::SCHEME_MAILTO, "mailto:", 25 },
2190     { URI::SCHEME_NEWS,   "news:",  117 },
2191     { URI::SCHEME_TELNET, "telnet:", 23 },
2192     { 0,                  NULL,       0 }
2193 };
2196 String URI::toString() const
2198     String str = schemeStr;
2199     if (authority.size() > 0)
2200         {
2201         str.append("//");
2202         str.append(authority);
2203         }
2204     str.append(path);
2205     if (query.size() > 0)
2206         {
2207         str.append("?");
2208         str.append(query);
2209         }
2210     if (fragment.size() > 0)
2211         {
2212         str.append("#");
2213         str.append(fragment);
2214         }
2215     return str;
2219 int URI::getScheme() const
2221     return scheme;
2224 String URI::getSchemeStr() const
2226     return schemeStr;
2230 String URI::getAuthority() const
2232     String ret = authority;
2233     if (portSpecified && port>=0)
2234         {
2235         char buf[7];
2236         snprintf(buf, 6, ":%6d", port);
2237         ret.append(buf);
2238         }
2239     return ret;
2242 String URI::getHost() const
2244     return authority;
2247 int URI::getPort() const
2249     return port;
2253 String URI::getPath() const
2255     return path;
2258 String URI::getNativePath() const
2260     String npath;
2261 #ifdef __WIN32__
2262     unsigned int firstChar = 0;
2263     if (path.size() >= 3)
2264         {
2265         if (path[0] == '/' &&
2266             isLetter(path[1]) &&
2267             path[2] == ':')
2268             firstChar++;
2269          }
2270     for (unsigned int i=firstChar ; i<path.size() ; i++)
2271         {
2272         XMLCh ch = (XMLCh) path[i];
2273         if (ch == '/')
2274             npath.push_back((XMLCh)'\\');
2275         else
2276             npath.push_back(ch);
2277         }
2278 #else
2279     npath = path;
2280 #endif
2281     return npath;
2285 bool URI::isAbsolute() const
2287     return absolute;
2290 bool URI::isOpaque() const
2292     return opaque;
2296 String URI::getQuery() const
2298     return query;
2302 String URI::getFragment() const
2304     return fragment;
2308 URI URI::resolve(const URI &other) const
2310     //### According to w3c, this is handled in 3 cases
2312     //## 1
2313     if (opaque || other.isAbsolute())
2314         return other;
2316     //## 2
2317     if (other.fragment.size()  >  0 &&
2318         other.path.size()      == 0 &&
2319         other.scheme           == SCHEME_NONE &&
2320         other.authority.size() == 0 &&
2321         other.query.size()     == 0 )
2322         {
2323         URI fragUri = *this;
2324         fragUri.fragment = other.fragment;
2325         return fragUri;
2326         }
2328     //## 3 http://www.ietf.org/rfc/rfc2396.txt, section 5.2
2329     URI newUri;
2330     //# 3.1
2331     newUri.scheme    = scheme;
2332     newUri.schemeStr = schemeStr;
2333     newUri.query     = other.query;
2334     newUri.fragment  = other.fragment;
2335     if (other.authority.size() > 0)
2336         {
2337         //# 3.2
2338         if (absolute || other.absolute)
2339             newUri.absolute = true;
2340         newUri.authority = other.authority;
2341         newUri.port      = other.port;//part of authority
2342         newUri.path      = other.path;
2343         }
2344     else
2345         {
2346         //# 3.3
2347         if (other.absolute)
2348             {
2349             newUri.absolute = true;
2350             newUri.path     = other.path;
2351             }
2352         else
2353             {
2354             unsigned int pos = path.find_last_of('/');
2355             if (pos != path.npos)
2356                 {
2357                 String tpath = path.substr(0, pos+1);
2358                 tpath.append(other.path);
2359                 newUri.path = tpath;
2360                 }
2361             else
2362                 newUri.path = other.path;
2363             }
2364         }
2366     newUri.normalize();
2367     return newUri;
2372 /**
2373  *  This follows the Java URI algorithm:
2374  *   1. All "." segments are removed.
2375  *   2. If a ".." segment is preceded by a non-".." segment
2376  *          then both of these segments are removed. This step
2377  *          is repeated until it is no longer applicable.
2378  *   3. If the path is relative, and if its first segment
2379  *          contains a colon character (':'), then a "." segment
2380  *          is prepended. This prevents a relative URI with a path
2381  *          such as "a:b/c/d" from later being re-parsed as an
2382  *          opaque URI with a scheme of "a" and a scheme-specific
2383  *          part of "b/c/d". (Deviation from RFC 2396)
2384  */
2385 void URI::normalize()
2387     std::vector<String> segments;
2389     //## Collect segments
2390     if (path.size()<2)
2391         return;
2392     bool abs = false;
2393     unsigned int pos=0;
2394     if (path[0]=='/')
2395         {
2396         abs = true;
2397         pos++;
2398         }
2399     while (pos < path.size())
2400         {
2401         unsigned int pos2 = path.find('/', pos);
2402         if (pos2==path.npos)
2403             {
2404             String seg = path.substr(pos);
2405             //printf("last segment:%s\n", seg.c_str());
2406             segments.push_back(seg);
2407             break;
2408             }
2409         if (pos2>pos)
2410             {
2411             String seg = path.substr(pos, pos2-pos);
2412             //printf("segment:%s\n", seg.c_str());
2413             segments.push_back(seg);
2414             }
2415         pos = pos2;
2416         pos++;
2417         }
2419     //## Clean up (normalize) segments
2420     bool edited = false;
2421     std::vector<String>::iterator iter;
2422     for (iter=segments.begin() ; iter!=segments.end() ; )
2423         {
2424         String s = *iter;
2425         if (s == ".")
2426             {
2427             iter = segments.erase(iter);
2428             edited = true;
2429             }
2430         else if (s == ".." &&
2431                  iter != segments.begin() &&
2432                  *(iter-1) != "..")
2433             {
2434             iter--; //back up, then erase two entries
2435             iter = segments.erase(iter);
2436             iter = segments.erase(iter);
2437             edited = true;
2438             }
2439         else
2440             iter++;
2441         }
2443     //## Rebuild path, if necessary
2444     if (edited)
2445         {
2446         path.clear();
2447         if (abs)
2448             {
2449             path.append("/");
2450             }
2451         std::vector<String>::iterator iter;
2452         for (iter=segments.begin() ; iter!=segments.end() ; iter++)
2453             {
2454             if (iter != segments.begin())
2455                 path.append("/");
2456             path.append(*iter);
2457             }
2458         }
2464 //#########################################################################
2465 //# M E S S A G E S
2466 //#########################################################################
2468 void URI::error(const char *fmt, ...)
2470     va_list args;
2471     fprintf(stderr, "URI error: ");
2472     va_start(args, fmt);
2473     vfprintf(stderr, fmt, args);
2474     va_end(args);
2475     fprintf(stderr, "\n");
2478 void URI::trace(const char *fmt, ...)
2480     va_list args;
2481     fprintf(stdout, "URI: ");
2482     va_start(args, fmt);
2483     vfprintf(stdout, fmt, args);
2484     va_end(args);
2485     fprintf(stdout, "\n");
2491 //#########################################################################
2492 //# P A R S I N G
2493 //#########################################################################
2497 int URI::peek(int p)
2499     if (p<0 || p>=parselen)
2500         return -1;
2501     return parsebuf[p];
2506 int URI::match(int p0, const char *key)
2508     int p = p0;
2509     while (p < parselen)
2510         {
2511         if (*key == '\0')
2512             return p;
2513         else if (*key != parsebuf[p])
2514             break;
2515         p++; key++;
2516         }
2517     return p0;
2520 //#########################################################################
2521 //#  Parsing is performed according to:
2522 //#  http://www.gbiv.com/protocols/uri/rfc/rfc3986.html#components
2523 //#########################################################################
2525 int URI::parseScheme(int p0)
2527     int p = p0;
2528     for (LookupEntry *entry = schemes; entry->sval ; entry++)
2529         {
2530         int p2 = match(p, entry->sval);
2531         if (p2 > p)
2532             {
2533             schemeStr = entry->sval;
2534             scheme    = entry->ival;
2535             port      = entry->port;
2536             p = p2;
2537             return p;
2538             }
2539         }
2541     return p;
2545 int URI::parseHierarchicalPart(int p0)
2547     int p = p0;
2548     int ch;
2550     //# Authority field (host and port, for example)
2551     int p2 = match(p, "//");
2552     if (p2 > p)
2553         {
2554         p = p2;
2555         portSpecified = false;
2556         String portStr;
2557         while (p < parselen)
2558             {
2559             ch = peek(p);
2560             if (ch == '/')
2561                 break;
2562             else if (ch == ':')
2563                 portSpecified = true;
2564             else if (portSpecified)
2565                 portStr.push_back((XMLCh)ch);
2566             else
2567                 authority.push_back((XMLCh)ch);
2568             p++;
2569             }
2570         if (portStr.size() > 0)
2571             {
2572             char *pstr = (char *)portStr.c_str();
2573             char *endStr;
2574             long val = strtol(pstr, &endStr, 10);
2575             if (endStr > pstr) //successful parse?
2576                 port = val;
2577             }
2578         }
2580     //# Are we absolute?
2581     ch = peek(p);
2582     if (isLetter(ch) && peek(p+1)==':')
2583         {
2584         absolute = true;
2585         path.push_back((XMLCh)'/');
2586         }
2587     else if (ch == '/')
2588         {
2589         absolute = true;
2590         if (p>p0) //in other words, if '/' is not the first char
2591             opaque = true;
2592         path.push_back((XMLCh)ch);
2593         p++;
2594         }
2596     while (p < parselen)
2597         {
2598         ch = peek(p);
2599         if (ch == '?' || ch == '#')
2600             break;
2601         path.push_back((XMLCh)ch);
2602         p++;
2603         }
2605     return p;
2608 int URI::parseQuery(int p0)
2610     int p = p0;
2611     int ch = peek(p);
2612     if (ch != '?')
2613         return p0;
2615     p++;
2616     while (p < parselen)
2617         {
2618         ch = peek(p);
2619         if (ch == '#')
2620             break;
2621         query.push_back((XMLCh)ch);
2622         p++;
2623         }
2626     return p;
2629 int URI::parseFragment(int p0)
2632     int p = p0;
2633     int ch = peek(p);
2634     if (ch != '#')
2635         return p0;
2637     p++;
2638     while (p < parselen)
2639         {
2640         ch = peek(p);
2641         if (ch == '?')
2642             break;
2643         fragment.push_back((XMLCh)ch);
2644         p++;
2645         }
2648     return p;
2652 int URI::parse(int p0)
2655     int p = p0;
2657     int p2 = parseScheme(p);
2658     if (p2 < 0)
2659         {
2660         error("Scheme");
2661         return -1;
2662         }
2663     p = p2;
2666     p2 = parseHierarchicalPart(p);
2667     if (p2 < 0)
2668         {
2669         error("Hierarchical part");
2670         return -1;
2671         }
2672     p = p2;
2674     p2 = parseQuery(p);
2675     if (p2 < 0)
2676         {
2677         error("Query");
2678         return -1;
2679         }
2680     p = p2;
2683     p2 = parseFragment(p);
2684     if (p2 < 0)
2685         {
2686         error("Fragment");
2687         return -1;
2688         }
2689     p = p2;
2691     return p;
2697 bool URI::parse(const String &str)
2699     init();
2700     
2701     parselen = str.size();
2703     String tmp;
2704     for (unsigned int i=0 ; i<str.size() ; i++)
2705         {
2706         XMLCh ch = (XMLCh) str[i];
2707         if (ch == '\\')
2708             tmp.push_back((XMLCh)'/');
2709         else
2710             tmp.push_back(ch);
2711         }
2712     parsebuf = (char *) tmp.c_str();
2715     int p = parse(0);
2716     normalize();
2718     if (p < 0)
2719         {
2720         error("Syntax error");
2721         return false;
2722         }
2724     //printf("uri:%s\n", toString().c_str());
2725     //printf("path:%s\n", path.c_str());
2727     return true;
2738 //########################################################################
2739 //########################################################################
2740 //##  M A K E
2741 //########################################################################
2742 //########################################################################
2744 //########################################################################
2745 //# F I L E S E T
2746 //########################################################################
2747 /**
2748  * This is the descriptor for a <fileset> item
2749  */
2750 class FileSet
2752 public:
2754     /**
2755      *
2756      */
2757     FileSet()
2758         {}
2760     /**
2761      *
2762      */
2763     FileSet(const FileSet &other)
2764         { assign(other); }
2766     /**
2767      *
2768      */
2769     FileSet &operator=(const FileSet &other)
2770         { assign(other); return *this; }
2772     /**
2773      *
2774      */
2775     virtual ~FileSet()
2776         {}
2778     /**
2779      *
2780      */
2781     String getDirectory()
2782         { return directory; }
2783         
2784     /**
2785      *
2786      */
2787     void setDirectory(const String &val)
2788         { directory = val; }
2790     /**
2791      *
2792      */
2793     void setFiles(const std::vector<String> &val)
2794         { files = val; }
2796     /**
2797      *
2798      */
2799     std::vector<String> getFiles()
2800         { return files; }
2801         
2802     /**
2803      *
2804      */
2805     void setIncludes(const std::vector<String> &val)
2806         { includes = val; }
2808     /**
2809      *
2810      */
2811     std::vector<String> getIncludes()
2812         { return includes; }
2813         
2814     /**
2815      *
2816      */
2817     void setExcludes(const std::vector<String> &val)
2818         { excludes = val; }
2820     /**
2821      *
2822      */
2823     std::vector<String> getExcludes()
2824         { return excludes; }
2825         
2826     /**
2827      *
2828      */
2829     unsigned int size()
2830         { return files.size(); }
2831         
2832     /**
2833      *
2834      */
2835     String operator[](int index)
2836         { return files[index]; }
2837         
2838     /**
2839      *
2840      */
2841     void clear()
2842         {
2843         directory = "";
2844         files.clear();
2845         includes.clear();
2846         excludes.clear();
2847         }
2848         
2850 private:
2852     void assign(const FileSet &other)
2853         {
2854         directory = other.directory;
2855         files     = other.files;
2856         includes  = other.includes;
2857         excludes  = other.excludes;
2858         }
2860     String directory;
2861     std::vector<String> files;
2862     std::vector<String> includes;
2863     std::vector<String> excludes;
2864 };
2867 //########################################################################
2868 //# F I L E L I S T
2869 //########################################################################
2870 /**
2871  * This is a simpler, explicitly-named list of files
2872  */
2873 class FileList
2875 public:
2877     /**
2878      *
2879      */
2880     FileList()
2881         {}
2883     /**
2884      *
2885      */
2886     FileList(const FileList &other)
2887         { assign(other); }
2889     /**
2890      *
2891      */
2892     FileList &operator=(const FileList &other)
2893         { assign(other); return *this; }
2895     /**
2896      *
2897      */
2898     virtual ~FileList()
2899         {}
2901     /**
2902      *
2903      */
2904     String getDirectory()
2905         { return directory; }
2906         
2907     /**
2908      *
2909      */
2910     void setDirectory(const String &val)
2911         { directory = val; }
2913     /**
2914      *
2915      */
2916     void setFiles(const std::vector<String> &val)
2917         { files = val; }
2919     /**
2920      *
2921      */
2922     std::vector<String> getFiles()
2923         { return files; }
2924         
2925     /**
2926      *
2927      */
2928     unsigned int size()
2929         { return files.size(); }
2930         
2931     /**
2932      *
2933      */
2934     String operator[](int index)
2935         { return files[index]; }
2936         
2937     /**
2938      *
2939      */
2940     void clear()
2941         {
2942         directory = "";
2943         files.clear();
2944         }
2945         
2947 private:
2949     void assign(const FileList &other)
2950         {
2951         directory = other.directory;
2952         files     = other.files;
2953         }
2955     String directory;
2956     std::vector<String> files;
2957 };
2962 //########################################################################
2963 //# M A K E    B A S E
2964 //########################################################################
2965 /**
2966  * Base class for all classes in this file
2967  */
2968 class MakeBase
2970 public:
2972     MakeBase()
2973         { line = 0; }
2974     virtual ~MakeBase()
2975         {}
2977     /**
2978      *     Return the URI of the file associated with this object 
2979      */     
2980     URI getURI()
2981         { return uri; }
2983     /**
2984      * Set the uri to the given string
2985      */
2986     void setURI(const String &uristr)
2987         { uri.parse(uristr); }
2989     /**
2990      *  Resolve another path relative to this one
2991      */
2992     String resolve(const String &otherPath);
2994     /**
2995      * replace variable refs like ${a} with their values
2996      * Assume that the string has already been syntax validated
2997      */
2998     String eval(const String &s, const String &defaultVal);
3000     /**
3001      * replace variable refs like ${a} with their values
3002      * return true or false
3003      * Assume that the string has already been syntax validated
3004      */
3005     bool evalBool(const String &s, bool defaultVal);
3007     /**
3008      *  Get an element attribute, performing substitutions if necessary
3009      */
3010     bool getAttribute(Element *elem, const String &name, String &result);
3012     /**
3013      * Get an element value, performing substitutions if necessary
3014      */
3015     bool getValue(Element *elem, String &result);
3016     
3017     /**
3018      * Set the current line number in the file
3019      */         
3020     void setLine(int val)
3021         { line = val; }
3022         
3023     /**
3024      * Get the current line number in the file
3025      */         
3026     int getLine()
3027         { return line; }
3030     /**
3031      * Set a property to a given value
3032      */
3033     virtual void setProperty(const String &name, const String &val)
3034         {
3035         properties[name] = val;
3036         }
3038     /**
3039      * Return a named property is found, else a null string
3040      */
3041     virtual String getProperty(const String &name)
3042         {
3043         String val;
3044         std::map<String, String>::iterator iter = properties.find(name);
3045         if (iter != properties.end())
3046             val = iter->second;
3047         String sval;
3048         if (!getSubstitutions(val, sval))
3049             return false;
3050         return sval;
3051         }
3053     /**
3054      * Return true if a named property is found, else false
3055      */
3056     virtual bool hasProperty(const String &name)
3057         {
3058         std::map<String, String>::iterator iter = properties.find(name);
3059         if (iter == properties.end())
3060             return false;
3061         return true;
3062         }
3065 protected:
3067     /**
3068      *    The path to the file associated with this object
3069      */     
3070     URI uri;
3071     
3072     /**
3073      *    If this prefix is seen in a substitution, use an environment
3074      *    variable.
3075      *             example:  <property environment="env"/>
3076      *             ${env.JAVA_HOME}
3077      */
3078     String envPrefix;
3080     /**
3081      *    If this prefix is seen in a substitution, use as a
3082      *    pkg-config 'all' query
3083      *             example:  <property pkg-config="pc"/>
3084      *             ${pc.gtkmm}
3085      */
3086     String pcPrefix;
3088     /**
3089      *    If this prefix is seen in a substitution, use as a
3090      *    pkg-config 'cflags' query
3091      *             example:  <property pkg-config="pcc"/>
3092      *             ${pcc.gtkmm}
3093      */
3094     String pccPrefix;
3096     /**
3097      *    If this prefix is seen in a substitution, use as a
3098      *    pkg-config 'libs' query
3099      *             example:  <property pkg-config="pcl"/>
3100      *             ${pcl.gtkmm}
3101      */
3102     String pclPrefix;
3108     /**
3109      *  Print a printf()-like formatted error message
3110      */
3111     void error(const char *fmt, ...);
3113     /**
3114      *  Print a printf()-like formatted trace message
3115      */
3116     void status(const char *fmt, ...);
3118     /**
3119      *  Show target status
3120      */
3121     void targetstatus(const char *fmt, ...);
3123     /**
3124      *  Print a printf()-like formatted trace message
3125      */
3126     void trace(const char *fmt, ...);
3128     /**
3129      *  Check if a given string matches a given regex pattern
3130      */
3131     bool regexMatch(const String &str, const String &pattern);
3133     /**
3134      *
3135      */
3136     String getSuffix(const String &fname);
3138     /**
3139      * Break up a string into substrings delimited the characters
3140      * in delimiters.  Null-length substrings are ignored
3141      */  
3142     std::vector<String> tokenize(const String &val,
3143                           const String &delimiters);
3145     /**
3146      *  replace runs of whitespace with a space
3147      */
3148     String strip(const String &s);
3150     /**
3151      *  remove leading whitespace from each line
3152      */
3153     String leftJustify(const String &s);
3155     /**
3156      *  remove leading and trailing whitespace from string
3157      */
3158     String trim(const String &s);
3160     /**
3161      *  Return a lower case version of the given string
3162      */
3163     String toLower(const String &s);
3165     /**
3166      * Return the native format of the canonical
3167      * path which we store
3168      */
3169     String getNativePath(const String &path);
3171     /**
3172      * Execute a shell command.  Outbuf is a ref to a string
3173      * to catch the result.     
3174      */         
3175     bool executeCommand(const String &call,
3176                         const String &inbuf,
3177                         String &outbuf,
3178                         String &errbuf);
3179     /**
3180      * List all directories in a given base and starting directory
3181      * It is usually called like:
3182      *        bool ret = listDirectories("src", "", result);    
3183      */         
3184     bool listDirectories(const String &baseName,
3185                          const String &dirname,
3186                          std::vector<String> &res);
3188     /**
3189      * Find all files in the named directory 
3190      */         
3191     bool listFiles(const String &baseName,
3192                    const String &dirname,
3193                    std::vector<String> &result);
3195     /**
3196      * Perform a listing for a fileset 
3197      */         
3198     bool listFiles(MakeBase &propRef, FileSet &fileSet);
3200     /**
3201      * Parse a <patternset>
3202      */  
3203     bool parsePatternSet(Element *elem,
3204                        MakeBase &propRef,
3205                        std::vector<String> &includes,
3206                        std::vector<String> &excludes);
3208     /**
3209      * Parse a <fileset> entry, and determine which files
3210      * should be included
3211      */  
3212     bool parseFileSet(Element *elem,
3213                     MakeBase &propRef,
3214                     FileSet &fileSet);
3215     /**
3216      * Parse a <filelist> entry
3217      */  
3218     bool parseFileList(Element *elem,
3219                     MakeBase &propRef,
3220                     FileList &fileList);
3222     /**
3223      * Return this object's property list
3224      */
3225     virtual std::map<String, String> &getProperties()
3226         { return properties; }
3229     std::map<String, String> properties;
3231     /**
3232      * Create a directory, making intermediate dirs
3233      * if necessary
3234      */                  
3235     bool createDirectory(const String &dirname);
3237     /**
3238      * Delete a directory and its children if desired
3239      */
3240     bool removeDirectory(const String &dirName);
3242     /**
3243      * Copy a file from one name to another. Perform only if needed
3244      */ 
3245     bool copyFile(const String &srcFile, const String &destFile);
3247     /**
3248      * Tests if the file exists and is a regular file
3249      */ 
3250     bool isRegularFile(const String &fileName);
3252     /**
3253      * Tests if the file exists and is a directory
3254      */ 
3255     bool isDirectory(const String &fileName);
3257     /**
3258      * Tests is the modification date of fileA is newer than fileB
3259      */ 
3260     bool isNewerThan(const String &fileA, const String &fileB);
3262 private:
3264     bool pkgConfigRecursive(const String packageName,
3265                             const String &path, 
3266                             const String &prefix, 
3267                             int query,
3268                             String &result,
3269                             std::set<String> &deplist);
3271     /**
3272      * utility method to query for "all", "cflags", or "libs" for this package and its
3273      * dependencies.  0, 1, 2
3274      */          
3275     bool pkgConfigQuery(const String &packageName, int query, String &result);
3277     /**
3278      * replace a variable ref like ${a} with a value
3279      */
3280     bool lookupProperty(const String &s, String &result);
3281     
3282     /**
3283      * called by getSubstitutions().  This is in case a looked-up string
3284      * has substitutions also.     
3285      */
3286     bool getSubstitutionsRecursive(const String &s, String &result, int depth);
3288     /**
3289      * replace variable refs in a string like ${a} with their values
3290      */
3291     bool getSubstitutions(const String &s, String &result);
3293     int line;
3296 };
3300 /**
3301  * Define the pkg-config class here, since it will be used in MakeBase method
3302  * implementations. 
3303  */
3304 class PkgConfig : public MakeBase
3307 public:
3309     /**
3310      *
3311      */
3312     PkgConfig()
3313         {
3314          path   = ".";
3315          prefix = "/target";
3316          init();
3317          }
3319     /**
3320      *
3321      */
3322     PkgConfig(const PkgConfig &other)
3323         { assign(other); }
3325     /**
3326      *
3327      */
3328     PkgConfig &operator=(const PkgConfig &other)
3329         { assign(other); return *this; }
3331     /**
3332      *
3333      */
3334     virtual ~PkgConfig()
3335         { }
3337     /**
3338      *
3339      */
3340     virtual String getName()
3341         { return name; }
3343     /**
3344      *
3345      */
3346     virtual String getPath()
3347         { return path; }
3349     /**
3350      *
3351      */
3352     virtual void setPath(const String &val)
3353         { path = val; }
3355     /**
3356      *
3357      */
3358     virtual String getPrefix()
3359         { return prefix; }
3361     /**
3362      *  Allow the user to override the prefix in the file
3363      */
3364     virtual void setPrefix(const String &val)
3365         { prefix = val; }
3367     /**
3368      *
3369      */
3370     virtual String getDescription()
3371         { return description; }
3373     /**
3374      *
3375      */
3376     virtual String getCflags()
3377         { return cflags; }
3379     /**
3380      *
3381      */
3382     virtual String getLibs()
3383         { return libs; }
3385     /**
3386      *
3387      */
3388     virtual String getAll()
3389         {
3390          String ret = cflags;
3391          ret.append(" ");
3392          ret.append(libs);
3393          return ret;
3394         }
3396     /**
3397      *
3398      */
3399     virtual String getVersion()
3400         { return version; }
3402     /**
3403      *
3404      */
3405     virtual int getMajorVersion()
3406         { return majorVersion; }
3408     /**
3409      *
3410      */
3411     virtual int getMinorVersion()
3412         { return minorVersion; }
3414     /**
3415      *
3416      */
3417     virtual int getMicroVersion()
3418         { return microVersion; }
3420     /**
3421      *
3422      */
3423     virtual std::map<String, String> &getAttributes()
3424         { return attrs; }
3426     /**
3427      *
3428      */
3429     virtual std::vector<String> &getRequireList()
3430         { return requireList; }
3432     /**
3433      *  Read a file for its details
3434      */         
3435     virtual bool readFile(const String &fileName);
3437     /**
3438      *  Read a file for its details
3439      */         
3440     virtual bool query(const String &name);
3442 private:
3444     void init()
3445         {
3446         //do not set path and prefix here
3447         name         = "";
3448         description  = "";
3449         cflags       = "";
3450         libs         = "";
3451         requires     = "";
3452         version      = "";
3453         majorVersion = 0;
3454         minorVersion = 0;
3455         microVersion = 0;
3456         fileName     = "";
3457         attrs.clear();
3458         requireList.clear();
3459         }
3461     void assign(const PkgConfig &other)
3462         {
3463         name         = other.name;
3464         path         = other.path;
3465         prefix       = other.prefix;
3466         description  = other.description;
3467         cflags       = other.cflags;
3468         libs         = other.libs;
3469         requires     = other.requires;
3470         version      = other.version;
3471         majorVersion = other.majorVersion;
3472         minorVersion = other.minorVersion;
3473         microVersion = other.microVersion;
3474         fileName     = other.fileName;
3475         attrs        = other.attrs;
3476         requireList  = other.requireList;
3477         }
3481     int get(int pos);
3483     int skipwhite(int pos);
3485     int getword(int pos, String &ret);
3487     /**
3488      * Very important
3489      */         
3490     bool parseRequires();
3492     void parseVersion();
3494     bool parseLine(const String &lineBuf);
3496     bool parse(const String &buf);
3498     void dumpAttrs();
3500     String name;
3502     String path;
3504     String prefix;
3506     String description;
3508     String cflags;
3510     String libs;
3512     String requires;
3514     String version;
3516     int majorVersion;
3518     int minorVersion;
3520     int microVersion;
3522     String fileName;
3524     std::map<String, String> attrs;
3526     std::vector<String> requireList;
3528     char *parsebuf;
3529     int parselen;
3530 };
3535 /**
3536  *  Print a printf()-like formatted error message
3537  */
3538 void MakeBase::error(const char *fmt, ...)
3540     va_list args;
3541     va_start(args,fmt);
3542     fprintf(stderr, "Make error line %d: ", line);
3543     vfprintf(stderr, fmt, args);
3544     fprintf(stderr, "\n");
3545     va_end(args) ;
3550 /**
3551  *  Print a printf()-like formatted trace message
3552  */
3553 void MakeBase::status(const char *fmt, ...)
3555     va_list args;
3556     //fprintf(stdout, " ");
3557     va_start(args,fmt);
3558     vfprintf(stdout, fmt, args);
3559     va_end(args);
3560     fprintf(stdout, "\n");
3561     fflush(stdout);
3565 /**
3566  *  Print a printf()-like formatted trace message
3567  */
3568 void MakeBase::trace(const char *fmt, ...)
3570     va_list args;
3571     fprintf(stdout, "Make: ");
3572     va_start(args,fmt);
3573     vfprintf(stdout, fmt, args);
3574     va_end(args) ;
3575     fprintf(stdout, "\n");
3576     fflush(stdout);
3581 /**
3582  *  Resolve another path relative to this one
3583  */
3584 String MakeBase::resolve(const String &otherPath)
3586     URI otherURI(otherPath);
3587     URI fullURI = uri.resolve(otherURI);
3588     String ret = fullURI.toString();
3589     return ret;
3594 /**
3595  *  Check if a given string matches a given regex pattern
3596  */
3597 bool MakeBase::regexMatch(const String &str, const String &pattern)
3599     const TRexChar *terror = NULL;
3600     const TRexChar *cpat = pattern.c_str();
3601     TRex *expr = trex_compile(cpat, &terror);
3602     if (!expr)
3603         {
3604         if (!terror)
3605             terror = "undefined";
3606         error("compilation error [%s]!\n", terror);
3607         return false;
3608         } 
3610     bool ret = true;
3612     const TRexChar *cstr = str.c_str();
3613     if (trex_match(expr, cstr))
3614         {
3615         ret = true;
3616         }
3617     else
3618         {
3619         ret = false;
3620         }
3622     trex_free(expr);
3624     return ret;
3627 /**
3628  *  Return the suffix, if any, of a file name
3629  */
3630 String MakeBase::getSuffix(const String &fname)
3632     if (fname.size() < 2)
3633         return "";
3634     unsigned int pos = fname.find_last_of('.');
3635     if (pos == fname.npos)
3636         return "";
3637     pos++;
3638     String res = fname.substr(pos, fname.size()-pos);
3639     //trace("suffix:%s", res.c_str()); 
3640     return res;
3645 /**
3646  * Break up a string into substrings delimited the characters
3647  * in delimiters.  Null-length substrings are ignored
3648  */  
3649 std::vector<String> MakeBase::tokenize(const String &str,
3650                                 const String &delimiters)
3653     std::vector<String> res;
3654     char *del = (char *)delimiters.c_str();
3655     String dmp;
3656     for (unsigned int i=0 ; i<str.size() ; i++)
3657         {
3658         char ch = str[i];
3659         char *p = (char *)0;
3660         for (p=del ; *p ; p++)
3661             if (*p == ch)
3662                 break;
3663         if (*p)
3664             {
3665             if (dmp.size() > 0)
3666                 {
3667                 res.push_back(dmp);
3668                 dmp.clear();
3669                 }
3670             }
3671         else
3672             {
3673             dmp.push_back(ch);
3674             }
3675         }
3676     //Add tail
3677     if (dmp.size() > 0)
3678         {
3679         res.push_back(dmp);
3680         dmp.clear();
3681         }
3683     return res;
3688 /**
3689  *  replace runs of whitespace with a single space
3690  */
3691 String MakeBase::strip(const String &s)
3693     int len = s.size();
3694     String stripped;
3695     for (int i = 0 ; i<len ; i++)
3696         {
3697         char ch = s[i];
3698         if (isspace(ch))
3699             {
3700             stripped.push_back(' ');
3701             for ( ; i<len ; i++)
3702                 {
3703                 ch = s[i];
3704                 if (!isspace(ch))
3705                     {
3706                     stripped.push_back(ch);
3707                     break;
3708                     }
3709                 }
3710             }
3711         else
3712             {
3713             stripped.push_back(ch);
3714             }
3715         }
3716     return stripped;
3719 /**
3720  *  remove leading whitespace from each line
3721  */
3722 String MakeBase::leftJustify(const String &s)
3724     String out;
3725     int len = s.size();
3726     for (int i = 0 ; i<len ; )
3727         {
3728         char ch;
3729         //Skip to first visible character
3730         while (i<len)
3731             {
3732             ch = s[i];
3733             if (ch == '\n' || ch == '\r'
3734               || !isspace(ch))
3735                   break;
3736             i++;
3737             }
3738         //Copy the rest of the line
3739         while (i<len)
3740             {
3741             ch = s[i];
3742             if (ch == '\n' || ch == '\r')
3743                 {
3744                 if (ch != '\r')
3745                     out.push_back('\n');
3746                 i++;
3747                 break;
3748                 }
3749             else
3750                 {
3751                 out.push_back(ch);
3752                 }
3753             i++;
3754             }
3755         }
3756     return out;
3760 /**
3761  *  Removes whitespace from beginning and end of a string
3762  */
3763 String MakeBase::trim(const String &s)
3765     if (s.size() < 1)
3766         return s;
3767     
3768     //Find first non-ws char
3769     unsigned int begin = 0;
3770     for ( ; begin < s.size() ; begin++)
3771         {
3772         if (!isspace(s[begin]))
3773             break;
3774         }
3776     //Find first non-ws char, going in reverse
3777     unsigned int end = s.size() - 1;
3778     for ( ; end > begin ; end--)
3779         {
3780         if (!isspace(s[end]))
3781             break;
3782         }
3783     //trace("begin:%d  end:%d", begin, end);
3785     String res = s.substr(begin, end-begin+1);
3786     return res;
3790 /**
3791  *  Return a lower case version of the given string
3792  */
3793 String MakeBase::toLower(const String &s)
3795     if (s.size()==0)
3796         return s;
3798     String ret;
3799     for(unsigned int i=0; i<s.size() ; i++)
3800         {
3801         ret.push_back(tolower(s[i]));
3802         }
3803     return ret;
3807 /**
3808  * Return the native format of the canonical
3809  * path which we store
3810  */
3811 String MakeBase::getNativePath(const String &path)
3813 #ifdef __WIN32__
3814     String npath;
3815     unsigned int firstChar = 0;
3816     if (path.size() >= 3)
3817         {
3818         if (path[0] == '/' &&
3819             isalpha(path[1]) &&
3820             path[2] == ':')
3821             firstChar++;
3822         }
3823     for (unsigned int i=firstChar ; i<path.size() ; i++)
3824         {
3825         char ch = path[i];
3826         if (ch == '/')
3827             npath.push_back('\\');
3828         else
3829             npath.push_back(ch);
3830         }
3831     return npath;
3832 #else
3833     return path;
3834 #endif
3838 #ifdef __WIN32__
3839 #include <tchar.h>
3841 static String win32LastError()
3844     DWORD dw = GetLastError(); 
3846     LPVOID str;
3847     FormatMessage(
3848         FORMAT_MESSAGE_ALLOCATE_BUFFER | 
3849         FORMAT_MESSAGE_FROM_SYSTEM,
3850         NULL,
3851         dw,
3852         0,
3853         (LPTSTR) &str,
3854         0, NULL );
3855     LPTSTR p = _tcschr((const char *)str, _T('\r'));
3856     if(p != NULL)
3857         { // lose CRLF
3858         *p = _T('\0');
3859         }
3860     String ret = (char *)str;
3861     LocalFree(str);
3863     return ret;
3865 #endif
3869 /**
3870  * Execute a system call, using pipes to send data to the
3871  * program's stdin,  and reading stdout and stderr.
3872  */
3873 bool MakeBase::executeCommand(const String &command,
3874                               const String &inbuf,
3875                               String &outbuf,
3876                               String &errbuf)
3879     status("============ cmd ============\n%s\n=============================",
3880                 command.c_str());
3882     outbuf.clear();
3883     errbuf.clear();
3884     
3885 #ifdef __WIN32__
3887     /*
3888     I really hate having win32 code in this program, but the
3889     read buffer in command.com and cmd.exe are just too small
3890     for the large commands we need for compiling and linking.
3891     */
3893     bool ret = true;
3895     //# Allocate a separate buffer for safety
3896     char *paramBuf = new char[command.size() + 1];
3897     if (!paramBuf)
3898        {
3899        error("executeCommand cannot allocate command buffer");
3900        return false;
3901        }
3902     strcpy(paramBuf, (char *)command.c_str());
3904     //# Go to http://msdn2.microsoft.com/en-us/library/ms682499.aspx
3905     //# to see how Win32 pipes work
3907     //# Create pipes
3908     SECURITY_ATTRIBUTES saAttr; 
3909     saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); 
3910     saAttr.bInheritHandle = TRUE; 
3911     saAttr.lpSecurityDescriptor = NULL; 
3912     HANDLE stdinRead,  stdinWrite;
3913     HANDLE stdoutRead, stdoutWrite;
3914     HANDLE stderrRead, stderrWrite;
3915     if (!CreatePipe(&stdinRead, &stdinWrite, &saAttr, 0))
3916         {
3917         error("executeProgram: could not create pipe");
3918         delete[] paramBuf;
3919         return false;
3920         } 
3921     SetHandleInformation(stdinWrite, HANDLE_FLAG_INHERIT, 0);
3922     if (!CreatePipe(&stdoutRead, &stdoutWrite, &saAttr, 0))
3923         {
3924         error("executeProgram: could not create pipe");
3925         delete[] paramBuf;
3926         return false;
3927         } 
3928     SetHandleInformation(stdoutRead, HANDLE_FLAG_INHERIT, 0);
3929     if (!CreatePipe(&stderrRead, &stderrWrite, &saAttr, 0))
3930         {
3931         error("executeProgram: could not create pipe");
3932         delete[] paramBuf;
3933         return false;
3934         } 
3935     SetHandleInformation(stderrRead, HANDLE_FLAG_INHERIT, 0);
3937     // Create the process
3938     STARTUPINFO siStartupInfo;
3939     PROCESS_INFORMATION piProcessInfo;
3940     memset(&siStartupInfo, 0, sizeof(siStartupInfo));
3941     memset(&piProcessInfo, 0, sizeof(piProcessInfo));
3942     siStartupInfo.cb = sizeof(siStartupInfo);
3943     siStartupInfo.hStdError   =  stderrWrite;
3944     siStartupInfo.hStdOutput  =  stdoutWrite;
3945     siStartupInfo.hStdInput   =  stdinRead;
3946     siStartupInfo.dwFlags    |=  STARTF_USESTDHANDLES;
3947    
3948     if (!CreateProcess(NULL, paramBuf, NULL, NULL, true,
3949                 0, NULL, NULL, &siStartupInfo,
3950                 &piProcessInfo))
3951         {
3952         error("executeCommand : could not create process : %s",
3953                     win32LastError().c_str());
3954         ret = false;
3955         }
3957     delete[] paramBuf;
3959     DWORD bytesWritten;
3960     if (inbuf.size()>0 &&
3961         !WriteFile(stdinWrite, inbuf.c_str(), inbuf.size(), 
3962                &bytesWritten, NULL))
3963         {
3964         error("executeCommand: could not write to pipe");
3965         return false;
3966         }    
3967     if (!CloseHandle(stdinWrite))
3968         {          
3969         error("executeCommand: could not close write pipe");
3970         return false;
3971         }
3972     if (!CloseHandle(stdoutWrite))
3973         {
3974         error("executeCommand: could not close read pipe");
3975         return false;
3976         }
3977     if (!CloseHandle(stderrWrite))
3978         {
3979         error("executeCommand: could not close read pipe");
3980         return false;
3981         }
3983     bool lastLoop = false;
3984     while (true)
3985         {
3986         DWORD avail;
3987         DWORD bytesRead;
3988         char readBuf[4096];
3990         //trace("## stderr");
3991         PeekNamedPipe(stderrRead, NULL, 0, NULL, &avail, NULL);
3992         if (avail > 0)
3993             {
3994             bytesRead = 0;
3995             if (avail>4096) avail = 4096;
3996             ReadFile(stderrRead, readBuf, avail, &bytesRead, NULL);
3997             if (bytesRead > 0)
3998                 {
3999                 for (unsigned int i=0 ; i<bytesRead ; i++)
4000                     errbuf.push_back(readBuf[i]);
4001                 }
4002             }
4004         //trace("## stdout");
4005         PeekNamedPipe(stdoutRead, NULL, 0, NULL, &avail, NULL);
4006         if (avail > 0)
4007             {
4008             bytesRead = 0;
4009             if (avail>4096) avail = 4096;
4010             ReadFile(stdoutRead, readBuf, avail, &bytesRead, NULL);
4011             if (bytesRead > 0)
4012                 {
4013                 for (unsigned int i=0 ; i<bytesRead ; i++)
4014                     outbuf.push_back(readBuf[i]);
4015                 }
4016             }
4017             
4018         //Was this the final check after program done?
4019         if (lastLoop)
4020             break;
4022         DWORD exitCode;
4023         GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
4024         if (exitCode != STILL_ACTIVE)
4025             lastLoop = true;
4027         Sleep(10);
4028         }    
4029     //trace("outbuf:%s", outbuf.c_str());
4030     if (!CloseHandle(stdoutRead))
4031         {
4032         error("executeCommand: could not close read pipe");
4033         return false;
4034         }
4035     if (!CloseHandle(stderrRead))
4036         {
4037         error("executeCommand: could not close read pipe");
4038         return false;
4039         }
4041     DWORD exitCode;
4042     GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
4043     //trace("exit code:%d", exitCode);
4044     if (exitCode != 0)
4045         {
4046         ret = false;
4047         }
4048     
4049     CloseHandle(piProcessInfo.hProcess);
4050     CloseHandle(piProcessInfo.hThread);
4052     return ret;
4054 #else //do it unix-style
4056     String pipeCommand = command;
4057     pipeCommand.append("  2>&1");
4058     String s;
4059     FILE *f = popen(pipeCommand.c_str(), "r");
4060     int errnum = 0;
4061     if (f)
4062         {
4063         while (true)
4064             {
4065             int ch = fgetc(f);
4066             if (ch < 0)
4067                 break;
4068             s.push_back((char)ch);
4069             }
4070         errnum = pclose(f);
4071         }
4072     outbuf = s;
4073     if (errnum != 0)
4074         {
4075         error("exec of command '%s' failed : %s",
4076              command.c_str(), strerror(errno));
4077         return false;
4078         }
4079     else
4080         return true;
4082 #endif
4083
4088 bool MakeBase::listDirectories(const String &baseName,
4089                               const String &dirName,
4090                               std::vector<String> &res)
4092     res.push_back(dirName);
4093     String fullPath = baseName;
4094     if (dirName.size()>0)
4095         {
4096         fullPath.append("/");
4097         fullPath.append(dirName);
4098         }
4099     DIR *dir = opendir(fullPath.c_str());
4100     while (true)
4101         {
4102         struct dirent *de = readdir(dir);
4103         if (!de)
4104             break;
4106         //Get the directory member name
4107         String s = de->d_name;
4108         if (s.size() == 0 || s[0] == '.')
4109             continue;
4110         String childName = dirName;
4111         childName.append("/");
4112         childName.append(s);
4114         String fullChildPath = baseName;
4115         fullChildPath.append("/");
4116         fullChildPath.append(childName);
4117         struct stat finfo;
4118         String childNative = getNativePath(fullChildPath);
4119         if (stat(childNative.c_str(), &finfo)<0)
4120             {
4121             error("cannot stat file:%s", childNative.c_str());
4122             }
4123         else if (S_ISDIR(finfo.st_mode))
4124             {
4125             //trace("directory: %s", childName.c_str());
4126             if (!listDirectories(baseName, childName, res))
4127                 return false;
4128             }
4129         }
4130     closedir(dir);
4132     return true;
4136 bool MakeBase::listFiles(const String &baseDir,
4137                          const String &dirName,
4138                          std::vector<String> &res)
4140     String fullDir = baseDir;
4141     if (dirName.size()>0)
4142         {
4143         fullDir.append("/");
4144         fullDir.append(dirName);
4145         }
4146     String dirNative = getNativePath(fullDir);
4148     std::vector<String> subdirs;
4149     DIR *dir = opendir(dirNative.c_str());
4150     if (!dir)
4151         {
4152         error("Could not open directory %s : %s",
4153               dirNative.c_str(), strerror(errno));
4154         return false;
4155         }
4156     while (true)
4157         {
4158         struct dirent *de = readdir(dir);
4159         if (!de)
4160             break;
4162         //Get the directory member name
4163         String s = de->d_name;
4164         if (s.size() == 0 || s[0] == '.')
4165             continue;
4166         String childName;
4167         if (dirName.size()>0)
4168             {
4169             childName.append(dirName);
4170             childName.append("/");
4171             }
4172         childName.append(s);
4173         String fullChild = baseDir;
4174         fullChild.append("/");
4175         fullChild.append(childName);
4176         
4177         if (isDirectory(fullChild))
4178             {
4179             //trace("directory: %s", childName.c_str());
4180             if (!listFiles(baseDir, childName, res))
4181                 return false;
4182             continue;
4183             }
4184         else if (!isRegularFile(fullChild))
4185             {
4186             error("unknown file:%s", childName.c_str());
4187             return false;
4188             }
4190        //all done!
4191         res.push_back(childName);
4193         }
4194     closedir(dir);
4196     return true;
4200 /**
4201  * Several different classes extend MakeBase.  By "propRef", we mean
4202  * the one holding the properties.  Likely "Make" itself
4203  */
4204 bool MakeBase::listFiles(MakeBase &propRef, FileSet &fileSet)
4206     //before doing the list,  resolve any property references
4207     //that might have been specified in the directory name, such as ${src}
4208     String fsDir = fileSet.getDirectory();
4209     String dir;
4210     if (!propRef.getSubstitutions(fsDir, dir))
4211         return false;
4212     String baseDir = propRef.resolve(dir);
4213     std::vector<String> fileList;
4214     if (!listFiles(baseDir, "", fileList))
4215         return false;
4217     std::vector<String> includes = fileSet.getIncludes();
4218     std::vector<String> excludes = fileSet.getExcludes();
4220     std::vector<String> incs;
4221     std::vector<String>::iterator iter;
4223     std::sort(fileList.begin(), fileList.end());
4225     //If there are <includes>, then add files to the output
4226     //in the order of the include list
4227     if (includes.size()==0)
4228         incs = fileList;
4229     else
4230         {
4231         for (iter = includes.begin() ; iter != includes.end() ; iter++)
4232             {
4233             String &pattern = *iter;
4234             std::vector<String>::iterator siter;
4235             for (siter = fileList.begin() ; siter != fileList.end() ; siter++)
4236                 {
4237                 String s = *siter;
4238                 if (regexMatch(s, pattern))
4239                     {
4240                     //trace("INCLUDED:%s", s.c_str());
4241                     incs.push_back(s);
4242                     }
4243                 }
4244             }
4245         }
4247     //Now trim off the <excludes>
4248     std::vector<String> res;
4249     for (iter = incs.begin() ; iter != incs.end() ; iter++)
4250         {
4251         String s = *iter;
4252         bool skipme = false;
4253         std::vector<String>::iterator siter;
4254         for (siter = excludes.begin() ; siter != excludes.end() ; siter++)
4255             {
4256             String &pattern = *siter;
4257             if (regexMatch(s, pattern))
4258                 {
4259                 //trace("EXCLUDED:%s", s.c_str());
4260                 skipme = true;
4261                 break;
4262                 }
4263             }
4264         if (!skipme)
4265             res.push_back(s);
4266         }
4267         
4268     fileSet.setFiles(res);
4270     return true;
4274 /**
4275  * 0 == all, 1 = cflags, 2 = libs
4276  */ 
4277 bool MakeBase::pkgConfigRecursive(const String packageName,
4278                                   const String &path, 
4279                                   const String &prefix, 
4280                                   int query,
4281                                   String &result,
4282                                   std::set<String> &deplist) 
4284     PkgConfig pkgConfig;
4285     if (path.size() > 0)
4286         pkgConfig.setPath(path);
4287     if (prefix.size() > 0)
4288         pkgConfig.setPrefix(prefix);
4289     if (!pkgConfig.query(packageName))
4290         return false;
4291     if (query == 0)
4292         result = pkgConfig.getAll();
4293     else if (query == 1)
4294         result = pkgConfig.getCflags();
4295     else
4296         result = pkgConfig.getLibs();
4297     deplist.insert(packageName);
4298     std::vector<String> list = pkgConfig.getRequireList();
4299     for (unsigned int i = 0 ; i<list.size() ; i++)
4300         {
4301         String depPkgName = list[i];
4302         if (deplist.find(depPkgName) != deplist.end())
4303             continue;
4304         String val;
4305         if (!pkgConfigRecursive(depPkgName, path, prefix, query, val, deplist))
4306             {
4307             error("Based on 'requires' attribute of package '%s'", packageName.c_str());
4308             return false;
4309             }
4310         result.append(" ");
4311         result.append(val);
4312         }
4314     return true;
4317 bool MakeBase::pkgConfigQuery(const String &packageName, int query, String &result)
4319     std::set<String> deplist;
4320     String path = getProperty("pkg-config-path");
4321     if (path.size()>0)
4322         path = resolve(path);
4323     String prefix = getProperty("pkg-config-prefix");
4324     String val;
4325     if (!pkgConfigRecursive(packageName, path, prefix, query, val, deplist))
4326         return false;
4327     result = val;
4328     return true;
4333 /**
4334  * replace a variable ref like ${a} with a value
4335  */
4336 bool MakeBase::lookupProperty(const String &propertyName, String &result)
4338     String varname = propertyName;
4339     if (envPrefix.size() > 0 &&
4340         varname.compare(0, envPrefix.size(), envPrefix) == 0)
4341         {
4342         varname = varname.substr(envPrefix.size());
4343         char *envstr = getenv(varname.c_str());
4344         if (!envstr)
4345             {
4346             error("environment variable '%s' not defined", varname.c_str());
4347             return false;
4348             }
4349         result = envstr;
4350         }
4351     else if (pcPrefix.size() > 0 &&
4352         varname.compare(0, pcPrefix.size(), pcPrefix) == 0)
4353         {
4354         varname = varname.substr(pcPrefix.size());
4355         String val;
4356         if (!pkgConfigQuery(varname, 0, val))
4357             return false;
4358         result = val;
4359         }
4360     else if (pccPrefix.size() > 0 &&
4361         varname.compare(0, pccPrefix.size(), pccPrefix) == 0)
4362         {
4363         varname = varname.substr(pccPrefix.size());
4364         String val;
4365         if (!pkgConfigQuery(varname, 1, val))
4366             return false;
4367         result = val;
4368         }
4369     else if (pclPrefix.size() > 0 &&
4370         varname.compare(0, pclPrefix.size(), pclPrefix) == 0)
4371         {
4372         varname = varname.substr(pclPrefix.size());
4373         String val;
4374         if (!pkgConfigQuery(varname, 2, val))
4375             return false;
4376         result = val;
4377         }
4378     else
4379         {
4380         std::map<String, String>::iterator iter;
4381         iter = properties.find(varname);
4382         if (iter != properties.end())
4383             {
4384             result = iter->second;
4385             }
4386         else
4387             {
4388             error("property '%s' not found", varname.c_str());
4389             return false;
4390             }
4391         }
4392     return true;
4398 /**
4399  * Analyse a string, looking for any substitutions or other
4400  * things that need resolution 
4401  */
4402 bool MakeBase::getSubstitutionsRecursive(const String &str,
4403                                          String &result, int depth)
4405     if (depth > 10)
4406         {
4407         error("nesting of substitutions too deep (>10) for '%s'",
4408                         str.c_str());
4409         return false;
4410         }
4411     String s = trim(str);
4412     int len = (int)s.size();
4413     String val;
4414     for (int i=0 ; i<len ; i++)
4415         {
4416         char ch = s[i];
4417         if (ch == '$' && s[i+1] == '{')
4418             {
4419             String varname;
4420             int j = i+2;
4421             for ( ; j<len ; j++)
4422                 {
4423                 ch = s[j];
4424                 if (ch == '$' && s[j+1] == '{')
4425                     {
4426                     error("attribute %s cannot have nested variable references",
4427                            s.c_str());
4428                     return false;
4429                     }
4430                 else if (ch == '}')
4431                     {
4432                     varname = trim(varname);
4433                     String varval;
4434                     if (!lookupProperty(varname, varval))
4435                         return false;
4436                     String varval2;
4437                     //Now see if the answer has ${} in it, too
4438                     if (!getSubstitutionsRecursive(varval, varval2, depth + 1))
4439                         return false;
4440                     val.append(varval2);
4441                     break;
4442                     }
4443                 else
4444                     {
4445                     varname.push_back(ch);
4446                     }
4447                 }
4448             i = j;
4449             }
4450         else
4451             {
4452             val.push_back(ch);
4453             }
4454         }
4455     result = val;
4456     return true;
4459 /**
4460  * Analyse a string, looking for any substitutions or other
4461  * things that need resilution 
4462  */
4463 bool MakeBase::getSubstitutions(const String &str, String &result)
4465     return getSubstitutionsRecursive(str, result, 0);
4470 /**
4471  * replace variable refs like ${a} with their values
4472  * Assume that the string has already been syntax validated
4473  */
4474 String MakeBase::eval(const String &s, const String &defaultVal)
4476     if (s.size()==0)
4477         return defaultVal;
4478     String ret;
4479     if (getSubstitutions(s, ret))
4480         return ret;
4481     else
4482         return defaultVal;
4486 /**
4487  * replace variable refs like ${a} with their values
4488  * return true or false
4489  * Assume that the string has already been syntax validated
4490  */
4491 bool MakeBase::evalBool(const String &s, bool defaultVal)
4493     if (s.size()==0)
4494         return defaultVal;
4495     String val = eval(s, "false");
4496     if (s == "true" || s == "TRUE")
4497         return true;
4498     else
4499         return defaultVal;
4503 /**
4504  * Get a string attribute, testing it for proper syntax and
4505  * property names.
4506  */
4507 bool MakeBase::getAttribute(Element *elem, const String &name,
4508                                     String &result)
4510     String s = elem->getAttribute(name);
4511     String tmp;
4512     bool ret = getSubstitutions(s, tmp);
4513     if (ret)
4514         result = s;  //assign -if- ok
4515     return ret;
4519 /**
4520  * Get a string value, testing it for proper syntax and
4521  * property names.
4522  */
4523 bool MakeBase::getValue(Element *elem, String &result)
4525     String s = elem->getValue();
4526     String tmp;
4527     bool ret = getSubstitutions(s, tmp);
4528     if (ret)
4529         result = s;  //assign -if- ok
4530     return ret;
4536 /**
4537  * Parse a <patternset> entry
4538  */  
4539 bool MakeBase::parsePatternSet(Element *elem,
4540                           MakeBase &propRef,
4541                           std::vector<String> &includes,
4542                           std::vector<String> &excludes
4543                           )
4545     std::vector<Element *> children  = elem->getChildren();
4546     for (unsigned int i=0 ; i<children.size() ; i++)
4547         {
4548         Element *child = children[i];
4549         String tagName = child->getName();
4550         if (tagName == "exclude")
4551             {
4552             String fname;
4553             if (!propRef.getAttribute(child, "name", fname))
4554                 return false;
4555             //trace("EXCLUDE: %s", fname.c_str());
4556             excludes.push_back(fname);
4557             }
4558         else if (tagName == "include")
4559             {
4560             String fname;
4561             if (!propRef.getAttribute(child, "name", fname))
4562                 return false;
4563             //trace("INCLUDE: %s", fname.c_str());
4564             includes.push_back(fname);
4565             }
4566         }
4568     return true;
4574 /**
4575  * Parse a <fileset> entry, and determine which files
4576  * should be included
4577  */  
4578 bool MakeBase::parseFileSet(Element *elem,
4579                           MakeBase &propRef,
4580                           FileSet &fileSet)
4582     String name = elem->getName();
4583     if (name != "fileset")
4584         {
4585         error("expected <fileset>");
4586         return false;
4587         }
4590     std::vector<String> includes;
4591     std::vector<String> excludes;
4593     //A fileset has one implied patternset
4594     if (!parsePatternSet(elem, propRef, includes, excludes))
4595         {
4596         return false;
4597         }
4598     //Look for child tags, including more patternsets
4599     std::vector<Element *> children  = elem->getChildren();
4600     for (unsigned int i=0 ; i<children.size() ; i++)
4601         {
4602         Element *child = children[i];
4603         String tagName = child->getName();
4604         if (tagName == "patternset")
4605             {
4606             if (!parsePatternSet(child, propRef, includes, excludes))
4607                 {
4608                 return false;
4609                 }
4610             }
4611         }
4613     String dir;
4614     //Now do the stuff
4615     //Get the base directory for reading file names
4616     if (!propRef.getAttribute(elem, "dir", dir))
4617         return false;
4619     fileSet.setDirectory(dir);
4620     fileSet.setIncludes(includes);
4621     fileSet.setExcludes(excludes);
4622     
4623     /*
4624     std::vector<String> fileList;
4625     if (dir.size() > 0)
4626         {
4627         String baseDir = propRef.resolve(dir);
4628         if (!listFiles(baseDir, "", includes, excludes, fileList))
4629             return false;
4630         }
4631     std::sort(fileList.begin(), fileList.end());
4632     result = fileList;
4633     */
4635     
4636     /*
4637     for (unsigned int i=0 ; i<result.size() ; i++)
4638         {
4639         trace("RES:%s", result[i].c_str());
4640         }
4641     */
4643     
4644     return true;
4647 /**
4648  * Parse a <filelist> entry.  This is far simpler than FileSet,
4649  * since no directory scanning is needed.  The file names are listed
4650  * explicitly.
4651  */  
4652 bool MakeBase::parseFileList(Element *elem,
4653                           MakeBase &propRef,
4654                           FileList &fileList)
4656     std::vector<String> fnames;
4657     //Look for child tags, namely "file"
4658     std::vector<Element *> children  = elem->getChildren();
4659     for (unsigned int i=0 ; i<children.size() ; i++)
4660         {
4661         Element *child = children[i];
4662         String tagName = child->getName();
4663         if (tagName == "file")
4664             {
4665             String fname = child->getAttribute("name");
4666             if (fname.size()==0)
4667                 {
4668                 error("<file> element requires name="" attribute");
4669                 return false;
4670                 }
4671             fnames.push_back(fname);
4672             }
4673         else
4674             {
4675             error("tag <%s> not allowed in <fileset>", tagName.c_str());
4676             return false;
4677             }
4678         }
4680     String dir;
4681     //Get the base directory for reading file names
4682     if (!propRef.getAttribute(elem, "dir", dir))
4683         return false;
4684     fileList.setDirectory(dir);
4685     fileList.setFiles(fnames);
4687     return true;
4692 /**
4693  * Create a directory, making intermediate dirs
4694  * if necessary
4695  */                  
4696 bool MakeBase::createDirectory(const String &dirname)
4698     //trace("## createDirectory: %s", dirname.c_str());
4699     //## first check if it exists
4700     struct stat finfo;
4701     String nativeDir = getNativePath(dirname);
4702     char *cnative = (char *) nativeDir.c_str();
4703 #ifdef __WIN32__
4704     if (strlen(cnative)==2 && cnative[1]==':')
4705         return true;
4706 #endif
4707     if (stat(cnative, &finfo)==0)
4708         {
4709         if (!S_ISDIR(finfo.st_mode))
4710             {
4711             error("mkdir: file %s exists but is not a directory",
4712                   cnative);
4713             return false;
4714             }
4715         else //exists
4716             {
4717             return true;
4718             }
4719         }
4721     //## 2: pull off the last path segment, if any,
4722     //## to make the dir 'above' this one, if necessary
4723     unsigned int pos = dirname.find_last_of('/');
4724     if (pos>0 && pos != dirname.npos)
4725         {
4726         String subpath = dirname.substr(0, pos);
4727         //A letter root (c:) ?
4728         if (!createDirectory(subpath))
4729             return false;
4730         }
4731         
4732     //## 3: now make
4733 #ifdef __WIN32__
4734     if (mkdir(cnative)<0)
4735 #else
4736     if (mkdir(cnative, S_IRWXU | S_IRWXG | S_IRWXO)<0)
4737 #endif
4738         {
4739         error("cannot make directory '%s' : %s",
4740                  cnative, strerror(errno));
4741         return false;
4742         }
4743         
4744     return true;
4748 /**
4749  * Remove a directory recursively
4750  */ 
4751 bool MakeBase::removeDirectory(const String &dirName)
4753     char *dname = (char *)dirName.c_str();
4755     DIR *dir = opendir(dname);
4756     if (!dir)
4757         {
4758         //# Let this fail nicely.
4759         return true;
4760         //error("error opening directory %s : %s", dname, strerror(errno));
4761         //return false;
4762         }
4763     
4764     while (true)
4765         {
4766         struct dirent *de = readdir(dir);
4767         if (!de)
4768             break;
4770         //Get the directory member name
4771         String s = de->d_name;
4772         if (s.size() == 0 || s[0] == '.')
4773             continue;
4774         String childName;
4775         if (dirName.size() > 0)
4776             {
4777             childName.append(dirName);
4778             childName.append("/");
4779             }
4780         childName.append(s);
4783         struct stat finfo;
4784         String childNative = getNativePath(childName);
4785         char *cnative = (char *)childNative.c_str();
4786         if (stat(cnative, &finfo)<0)
4787             {
4788             error("cannot stat file:%s", cnative);
4789             }
4790         else if (S_ISDIR(finfo.st_mode))
4791             {
4792             //trace("DEL dir: %s", childName.c_str());
4793             if (!removeDirectory(childName))
4794                 {
4795                 return false;
4796                 }
4797             }
4798         else if (!S_ISREG(finfo.st_mode))
4799             {
4800             //trace("not regular: %s", cnative);
4801             }
4802         else
4803             {
4804             //trace("DEL file: %s", childName.c_str());
4805             if (remove(cnative)<0)
4806                 {
4807                 error("error deleting %s : %s",
4808                      cnative, strerror(errno));
4809                 return false;
4810                 }
4811             }
4812         }
4813     closedir(dir);
4815     //Now delete the directory
4816     String native = getNativePath(dirName);
4817     if (rmdir(native.c_str())<0)
4818         {
4819         error("could not delete directory %s : %s",
4820             native.c_str() , strerror(errno));
4821         return false;
4822         }
4824     return true;
4825     
4829 /**
4830  * Copy a file from one name to another. Perform only if needed
4831  */ 
4832 bool MakeBase::copyFile(const String &srcFile, const String &destFile)
4834     //# 1 Check up-to-date times
4835     String srcNative = getNativePath(srcFile);
4836     struct stat srcinfo;
4837     if (stat(srcNative.c_str(), &srcinfo)<0)
4838         {
4839         error("source file %s for copy does not exist",
4840                  srcNative.c_str());
4841         return false;
4842         }
4844     String destNative = getNativePath(destFile);
4845     struct stat destinfo;
4846     if (stat(destNative.c_str(), &destinfo)==0)
4847         {
4848         if (destinfo.st_mtime >= srcinfo.st_mtime)
4849             return true;
4850         }
4851         
4852     //# 2 prepare a destination directory if necessary
4853     unsigned int pos = destFile.find_last_of('/');
4854     if (pos != destFile.npos)
4855         {
4856         String subpath = destFile.substr(0, pos);
4857         if (!createDirectory(subpath))
4858             return false;
4859         }
4861     //# 3 do the data copy
4862 #ifndef __WIN32__
4864     FILE *srcf = fopen(srcNative.c_str(), "rb");
4865     if (!srcf)
4866         {
4867         error("copyFile cannot open '%s' for reading", srcNative.c_str());
4868         return false;
4869         }
4870     FILE *destf = fopen(destNative.c_str(), "wb");
4871     if (!destf)
4872         {
4873         error("copyFile cannot open %s for writing", srcNative.c_str());
4874         return false;
4875         }
4877     while (!feof(srcf))
4878         {
4879         int ch = fgetc(srcf);
4880         if (ch<0)
4881             break;
4882         fputc(ch, destf);
4883         }
4885     fclose(destf);
4886     fclose(srcf);
4888 #else
4889     
4890     if (!CopyFile(srcNative.c_str(), destNative.c_str(), false))
4891         {
4892         error("copyFile from %s to %s failed",
4893              srcNative.c_str(), destNative.c_str());
4894         return false;
4895         }
4896         
4897 #endif /* __WIN32__ */
4900     return true;
4905 /**
4906  * Tests if the file exists and is a regular file
4907  */ 
4908 bool MakeBase::isRegularFile(const String &fileName)
4910     String native = getNativePath(fileName);
4911     struct stat finfo;
4912     
4913     //Exists?
4914     if (stat(native.c_str(), &finfo)<0)
4915         return false;
4918     //check the file mode
4919     if (!S_ISREG(finfo.st_mode))
4920         return false;
4922     return true;
4925 /**
4926  * Tests if the file exists and is a directory
4927  */ 
4928 bool MakeBase::isDirectory(const String &fileName)
4930     String native = getNativePath(fileName);
4931     struct stat finfo;
4932     
4933     //Exists?
4934     if (stat(native.c_str(), &finfo)<0)
4935         return false;
4938     //check the file mode
4939     if (!S_ISDIR(finfo.st_mode))
4940         return false;
4942     return true;
4947 /**
4948  * Tests is the modification of fileA is newer than fileB
4949  */ 
4950 bool MakeBase::isNewerThan(const String &fileA, const String &fileB)
4952     //trace("isNewerThan:'%s' , '%s'", fileA.c_str(), fileB.c_str());
4953     String nativeA = getNativePath(fileA);
4954     struct stat infoA;
4955     //IF source does not exist, NOT newer
4956     if (stat(nativeA.c_str(), &infoA)<0)
4957         {
4958         return false;
4959         }
4961     String nativeB = getNativePath(fileB);
4962     struct stat infoB;
4963     //IF dest does not exist, YES, newer
4964     if (stat(nativeB.c_str(), &infoB)<0)
4965         {
4966         return true;
4967         }
4969     //check the actual times
4970     if (infoA.st_mtime > infoB.st_mtime)
4971         {
4972         return true;
4973         }
4975     return false;
4979 //########################################################################
4980 //# P K G    C O N F I G
4981 //########################################################################
4984 /**
4985  * Get a character from the buffer at pos.  If out of range,
4986  * return -1 for safety
4987  */
4988 int PkgConfig::get(int pos)
4990     if (pos>parselen)
4991         return -1;
4992     return parsebuf[pos];
4997 /**
4998  *  Skip over all whitespace characters beginning at pos.  Return
4999  *  the position of the first non-whitespace character.
5000  *  Pkg-config is line-oriented, so check for newline
5001  */
5002 int PkgConfig::skipwhite(int pos)
5004     while (pos < parselen)
5005         {
5006         int ch = get(pos);
5007         if (ch < 0)
5008             break;
5009         if (!isspace(ch))
5010             break;
5011         pos++;
5012         }
5013     return pos;
5017 /**
5018  *  Parse the buffer beginning at pos, for a word.  Fill
5019  *  'ret' with the result.  Return the position after the
5020  *  word.
5021  */
5022 int PkgConfig::getword(int pos, String &ret)
5024     while (pos < parselen)
5025         {
5026         int ch = get(pos);
5027         if (ch < 0)
5028             break;
5029         if (!isalnum(ch) && ch != '_' && ch != '-' && ch != '+' && ch != '.')
5030             break;
5031         ret.push_back((char)ch);
5032         pos++;
5033         }
5034     return pos;
5037 bool PkgConfig::parseRequires()
5039     if (requires.size() == 0)
5040         return true;
5041     parsebuf = (char *)requires.c_str();
5042     parselen = requires.size();
5043     int pos = 0;
5044     while (pos < parselen)
5045         {
5046         pos = skipwhite(pos);
5047         String val;
5048         int pos2 = getword(pos, val);
5049         if (pos2 == pos)
5050             break;
5051         pos = pos2;
5052         //trace("val %s", val.c_str());
5053         requireList.push_back(val);
5054         }
5055     return true;
5059 static int getint(const String str)
5061     char *s = (char *)str.c_str();
5062     char *ends = NULL;
5063     long val = strtol(s, &ends, 10);
5064     if (ends == s)
5065         return 0L;
5066     else
5067         return val;
5070 void PkgConfig::parseVersion()
5072     if (version.size() == 0)
5073         return;
5074     String s1, s2, s3;
5075     unsigned int pos = 0;
5076     unsigned int pos2 = version.find('.', pos);
5077     if (pos2 == version.npos)
5078         {
5079         s1 = version;
5080         }
5081     else
5082         {
5083         s1 = version.substr(pos, pos2-pos);
5084         pos = pos2;
5085         pos++;
5086         if (pos < version.size())
5087             {
5088             pos2 = version.find('.', pos);
5089             if (pos2 == version.npos)
5090                 {
5091                 s2 = version.substr(pos, version.size()-pos);
5092                 }
5093             else
5094                 {
5095                 s2 = version.substr(pos, pos2-pos);
5096                 pos = pos2;
5097                 pos++;
5098                 if (pos < version.size())
5099                     s3 = version.substr(pos, pos2-pos);
5100                 }
5101             }
5102         }
5104     majorVersion = getint(s1);
5105     minorVersion = getint(s2);
5106     microVersion = getint(s3);
5107     //trace("version:%d.%d.%d", majorVersion,
5108     //          minorVersion, microVersion );
5112 bool PkgConfig::parseLine(const String &lineBuf)
5114     parsebuf = (char *)lineBuf.c_str();
5115     parselen = lineBuf.size();
5116     int pos = 0;
5117     
5118     while (pos < parselen)
5119         {
5120         String attrName;
5121         pos = skipwhite(pos);
5122         int ch = get(pos);
5123         if (ch == '#')
5124             {
5125             //comment.  eat the rest of the line
5126             while (pos < parselen)
5127                 {
5128                 ch = get(pos);
5129                 if (ch == '\n' || ch < 0)
5130                     break;
5131                 pos++;
5132                 }
5133             continue;
5134             }
5135         pos = getword(pos, attrName);
5136         if (attrName.size() == 0)
5137             continue;
5138         
5139         pos = skipwhite(pos);
5140         ch = get(pos);
5141         if (ch != ':' && ch != '=')
5142             {
5143             error("expected ':' or '='");
5144             return false;
5145             }
5146         pos++;
5147         pos = skipwhite(pos);
5148         String attrVal;
5149         while (pos < parselen)
5150             {
5151             ch = get(pos);
5152             if (ch == '\n' || ch < 0)
5153                 break;
5154             else if (ch == '$' && get(pos+1) == '{')
5155                 {
5156                 //#  this is a ${substitution}
5157                 pos += 2;
5158                 String subName;
5159                 while (pos < parselen)
5160                     {
5161                     ch = get(pos);
5162                     if (ch < 0)
5163                         {
5164                         error("unterminated substitution");
5165                         return false;
5166                         }
5167                     else if (ch == '}')
5168                         break;
5169                     else
5170                         subName.push_back((char)ch);
5171                     pos++;
5172                     }
5173                 //trace("subName:%s %s", subName.c_str(), prefix.c_str());
5174                 if (subName == "prefix" && prefix.size()>0)
5175                     {
5176                     attrVal.append(prefix);
5177                     //trace("prefix override:%s", prefix.c_str());
5178                     }
5179                 else
5180                     {
5181                     String subVal = attrs[subName];
5182                     //trace("subVal:%s", subVal.c_str());
5183                     attrVal.append(subVal);
5184                     }
5185                 }
5186             else
5187                 attrVal.push_back((char)ch);
5188             pos++;
5189             }
5191         attrVal = trim(attrVal);
5192         attrs[attrName] = attrVal;
5194         String attrNameL = toLower(attrName);
5196         if (attrNameL == "name")
5197             name = attrVal;
5198         else if (attrNameL == "description")
5199             description = attrVal;
5200         else if (attrNameL == "cflags")
5201             cflags = attrVal;
5202         else if (attrNameL == "libs")
5203             libs = attrVal;
5204         else if (attrNameL == "requires")
5205             requires = attrVal;
5206         else if (attrNameL == "version")
5207             version = attrVal;
5209         //trace("name:'%s'  value:'%s'",
5210         //      attrName.c_str(), attrVal.c_str());
5211         }
5213     return true;
5217 bool PkgConfig::parse(const String &buf)
5219     init();
5221     String line;
5222     int lineNr = 0;
5223     for (unsigned int p=0 ; p<buf.size() ; p++)
5224         {
5225         int ch = buf[p];
5226         if (ch == '\n' || ch == '\r')
5227             {
5228             if (!parseLine(line))
5229                 return false;
5230             line.clear();
5231             lineNr++;
5232             }
5233         else
5234             {
5235             line.push_back(ch);
5236             }
5237         }
5238     if (line.size()>0)
5239         {
5240         if (!parseLine(line))
5241             return false;
5242         }
5244     parseRequires();
5245     parseVersion();
5247     return true;
5253 void PkgConfig::dumpAttrs()
5255     //trace("### PkgConfig attributes for %s", fileName.c_str());
5256     std::map<String, String>::iterator iter;
5257     for (iter=attrs.begin() ; iter!=attrs.end() ; iter++)
5258         {
5259         trace("   %s = %s", iter->first.c_str(), iter->second.c_str());
5260         }
5264 bool PkgConfig::readFile(const String &fname)
5266     fileName = getNativePath(fname);
5268     FILE *f = fopen(fileName.c_str(), "r");
5269     if (!f)
5270         {
5271         error("cannot open file '%s' for reading", fileName.c_str());
5272         return false;
5273         }
5274     String buf;
5275     while (true)
5276         {
5277         int ch = fgetc(f);
5278         if (ch < 0)
5279             break;
5280         buf.push_back((char)ch);
5281         }
5282     fclose(f);
5284     //trace("####### File:\n%s", buf.c_str());
5285     if (!parse(buf))
5286         {
5287         return false;
5288         }
5290     //dumpAttrs();
5292     return true;
5297 bool PkgConfig::query(const String &pkgName)
5299     name = pkgName;
5301     String fname = path;
5302     fname.append("/");
5303     fname.append(name);
5304     fname.append(".pc");
5306     if (!readFile(fname))
5307         return false;
5308     
5309     return true;
5316 //########################################################################
5317 //# D E P T O O L
5318 //########################################################################
5322 /**
5323  *  Class which holds information for each file.
5324  */
5325 class FileRec
5327 public:
5329     typedef enum
5330         {
5331         UNKNOWN,
5332         CFILE,
5333         HFILE,
5334         OFILE
5335         } FileType;
5337     /**
5338      *  Constructor
5339      */
5340     FileRec()
5341         { init(); type = UNKNOWN; }
5343     /**
5344      *  Copy constructor
5345      */
5346     FileRec(const FileRec &other)
5347         { init(); assign(other); }
5348     /**
5349      *  Constructor
5350      */
5351     FileRec(int typeVal)
5352         { init(); type = typeVal; }
5353     /**
5354      *  Assignment operator
5355      */
5356     FileRec &operator=(const FileRec &other)
5357         { init(); assign(other); return *this; }
5360     /**
5361      *  Destructor
5362      */
5363     ~FileRec()
5364         {}
5366     /**
5367      *  Directory part of the file name
5368      */
5369     String path;
5371     /**
5372      *  Base name, sans directory and suffix
5373      */
5374     String baseName;
5376     /**
5377      *  File extension, such as cpp or h
5378      */
5379     String suffix;
5381     /**
5382      *  Type of file: CFILE, HFILE, OFILE
5383      */
5384     int type;
5386     /**
5387      * Used to list files ref'd by this one
5388      */
5389     std::map<String, FileRec *> files;
5392 private:
5394     void init()
5395         {
5396         }
5398     void assign(const FileRec &other)
5399         {
5400         type     = other.type;
5401         baseName = other.baseName;
5402         suffix   = other.suffix;
5403         files    = other.files;
5404         }
5406 };
5410 /**
5411  *  Simpler dependency record
5412  */
5413 class DepRec
5415 public:
5417     /**
5418      *  Constructor
5419      */
5420     DepRec()
5421         {init();}
5423     /**
5424      *  Copy constructor
5425      */
5426     DepRec(const DepRec &other)
5427         {init(); assign(other);}
5428     /**
5429      *  Constructor
5430      */
5431     DepRec(const String &fname)
5432         {init(); name = fname; }
5433     /**
5434      *  Assignment operator
5435      */
5436     DepRec &operator=(const DepRec &other)
5437         {init(); assign(other); return *this;}
5440     /**
5441      *  Destructor
5442      */
5443     ~DepRec()
5444         {}
5446     /**
5447      *  Directory part of the file name
5448      */
5449     String path;
5451     /**
5452      *  Base name, without the path and suffix
5453      */
5454     String name;
5456     /**
5457      *  Suffix of the source
5458      */
5459     String suffix;
5462     /**
5463      * Used to list files ref'd by this one
5464      */
5465     std::vector<String> files;
5468 private:
5470     void init()
5471         {
5472         }
5474     void assign(const DepRec &other)
5475         {
5476         path     = other.path;
5477         name     = other.name;
5478         suffix   = other.suffix;
5479         files    = other.files; //avoid recursion
5480         }
5482 };
5485 class DepTool : public MakeBase
5487 public:
5489     /**
5490      *  Constructor
5491      */
5492     DepTool()
5493         { init(); }
5495     /**
5496      *  Copy constructor
5497      */
5498     DepTool(const DepTool &other)
5499         { init(); assign(other); }
5501     /**
5502      *  Assignment operator
5503      */
5504     DepTool &operator=(const DepTool &other)
5505         { init(); assign(other); return *this; }
5508     /**
5509      *  Destructor
5510      */
5511     ~DepTool()
5512         {}
5515     /**
5516      *  Reset this section of code
5517      */
5518     virtual void init();
5519     
5520     /**
5521      *  Reset this section of code
5522      */
5523     virtual void assign(const DepTool &other)
5524         {
5525         }
5526     
5527     /**
5528      *  Sets the source directory which will be scanned
5529      */
5530     virtual void setSourceDirectory(const String &val)
5531         { sourceDir = val; }
5533     /**
5534      *  Returns the source directory which will be scanned
5535      */
5536     virtual String getSourceDirectory()
5537         { return sourceDir; }
5539     /**
5540      *  Sets the list of files within the directory to analyze
5541      */
5542     virtual void setFileList(const std::vector<String> &list)
5543         { fileList = list; }
5545     /**
5546      * Creates the list of all file names which will be
5547      * candidates for further processing.  Reads make.exclude
5548      * to see which files for directories to leave out.
5549      */
5550     virtual bool createFileList();
5553     /**
5554      *  Generates the forward dependency list
5555      */
5556     virtual bool generateDependencies();
5559     /**
5560      *  Generates the forward dependency list, saving the file
5561      */
5562     virtual bool generateDependencies(const String &);
5565     /**
5566      *  Load a dependency file
5567      */
5568     std::vector<DepRec> loadDepFile(const String &fileName);
5570     /**
5571      *  Load a dependency file, generating one if necessary
5572      */
5573     std::vector<DepRec> getDepFile(const String &fileName,
5574               bool forceRefresh);
5576     /**
5577      *  Save a dependency file
5578      */
5579     bool saveDepFile(const String &fileName);
5582 private:
5585     /**
5586      *
5587      */
5588     void parseName(const String &fullname,
5589                    String &path,
5590                    String &basename,
5591                    String &suffix);
5593     /**
5594      *
5595      */
5596     int get(int pos);
5598     /**
5599      *
5600      */
5601     int skipwhite(int pos);
5603     /**
5604      *
5605      */
5606     int getword(int pos, String &ret);
5608     /**
5609      *
5610      */
5611     bool sequ(int pos, const char *key);
5613     /**
5614      *
5615      */
5616     bool addIncludeFile(FileRec *frec, const String &fname);
5618     /**
5619      *
5620      */
5621     bool scanFile(const String &fname, FileRec *frec);
5623     /**
5624      *
5625      */
5626     bool processDependency(FileRec *ofile, FileRec *include);
5628     /**
5629      *
5630      */
5631     String sourceDir;
5633     /**
5634      *
5635      */
5636     std::vector<String> fileList;
5638     /**
5639      *
5640      */
5641     std::vector<String> directories;
5643     /**
5644      * A list of all files which will be processed for
5645      * dependencies.
5646      */
5647     std::map<String, FileRec *> allFiles;
5649     /**
5650      * The list of .o files, and the
5651      * dependencies upon them.
5652      */
5653     std::map<String, FileRec *> oFiles;
5655     int depFileSize;
5656     char *depFileBuf;
5658     static const int readBufSize = 8192;
5659     char readBuf[8193];//byte larger
5661 };
5667 /**
5668  *  Clean up after processing.  Called by the destructor, but should
5669  *  also be called before the object is reused.
5670  */
5671 void DepTool::init()
5673     sourceDir = ".";
5675     fileList.clear();
5676     directories.clear();
5677     
5678     //clear output file list
5679     std::map<String, FileRec *>::iterator iter;
5680     for (iter=oFiles.begin(); iter!=oFiles.end() ; iter++)
5681         delete iter->second;
5682     oFiles.clear();
5684     //allFiles actually contains the master copies. delete them
5685     for (iter= allFiles.begin(); iter!=allFiles.end() ; iter++)
5686         delete iter->second;
5687     allFiles.clear(); 
5694 /**
5695  *  Parse a full path name into path, base name, and suffix
5696  */
5697 void DepTool::parseName(const String &fullname,
5698                         String &path,
5699                         String &basename,
5700                         String &suffix)
5702     if (fullname.size() < 2)
5703         return;
5705     unsigned int pos = fullname.find_last_of('/');
5706     if (pos != fullname.npos && pos<fullname.size()-1)
5707         {
5708         path = fullname.substr(0, pos);
5709         pos++;
5710         basename = fullname.substr(pos, fullname.size()-pos);
5711         }
5712     else
5713         {
5714         path = "";
5715         basename = fullname;
5716         }
5718     pos = basename.find_last_of('.');
5719     if (pos != basename.npos && pos<basename.size()-1)
5720         {
5721         suffix   = basename.substr(pos+1, basename.size()-pos-1);
5722         basename = basename.substr(0, pos);
5723         }
5725     //trace("parsename:%s %s %s", path.c_str(),
5726     //        basename.c_str(), suffix.c_str()); 
5731 /**
5732  *  Generate our internal file list.
5733  */
5734 bool DepTool::createFileList()
5737     for (unsigned int i=0 ; i<fileList.size() ; i++)
5738         {
5739         String fileName = fileList[i];
5740         //trace("## FileName:%s", fileName.c_str());
5741         String path;
5742         String basename;
5743         String sfx;
5744         parseName(fileName, path, basename, sfx);
5745         if (sfx == "cpp" || sfx == "c" || sfx == "cxx"   ||
5746             sfx == "cc" || sfx == "CC")
5747             {
5748             FileRec *fe         = new FileRec(FileRec::CFILE);
5749             fe->path            = path;
5750             fe->baseName        = basename;
5751             fe->suffix          = sfx;
5752             allFiles[fileName]  = fe;
5753             }
5754         else if (sfx == "h"   ||  sfx == "hh"  ||
5755                  sfx == "hpp" ||  sfx == "hxx")
5756             {
5757             FileRec *fe         = new FileRec(FileRec::HFILE);
5758             fe->path            = path;
5759             fe->baseName        = basename;
5760             fe->suffix          = sfx;
5761             allFiles[fileName]  = fe;
5762             }
5763         }
5765     if (!listDirectories(sourceDir, "", directories))
5766         return false;
5767         
5768     return true;
5775 /**
5776  * Get a character from the buffer at pos.  If out of range,
5777  * return -1 for safety
5778  */
5779 int DepTool::get(int pos)
5781     if (pos>depFileSize)
5782         return -1;
5783     return depFileBuf[pos];
5788 /**
5789  *  Skip over all whitespace characters beginning at pos.  Return
5790  *  the position of the first non-whitespace character.
5791  */
5792 int DepTool::skipwhite(int pos)
5794     while (pos < depFileSize)
5795         {
5796         int ch = get(pos);
5797         if (ch < 0)
5798             break;
5799         if (!isspace(ch))
5800             break;
5801         pos++;
5802         }
5803     return pos;
5807 /**
5808  *  Parse the buffer beginning at pos, for a word.  Fill
5809  *  'ret' with the result.  Return the position after the
5810  *  word.
5811  */
5812 int DepTool::getword(int pos, String &ret)
5814     while (pos < depFileSize)
5815         {
5816         int ch = get(pos);
5817         if (ch < 0)
5818             break;
5819         if (isspace(ch))
5820             break;
5821         ret.push_back((char)ch);
5822         pos++;
5823         }
5824     return pos;
5827 /**
5828  * Return whether the sequence of characters in the buffer
5829  * beginning at pos match the key,  for the length of the key
5830  */
5831 bool DepTool::sequ(int pos, const char *key)
5833     while (*key)
5834         {
5835         if (*key != get(pos))
5836             return false;
5837         key++; pos++;
5838         }
5839     return true;
5844 /**
5845  *  Add an include file name to a file record.  If the name
5846  *  is not found in allFiles explicitly, try prepending include
5847  *  directory names to it and try again.
5848  */
5849 bool DepTool::addIncludeFile(FileRec *frec, const String &iname)
5851     //# if the name is an exact match to a path name
5852     //# in allFiles, like "myinc.h"
5853     std::map<String, FileRec *>::iterator iter =
5854            allFiles.find(iname);
5855     if (iter != allFiles.end()) //already exists
5856         {
5857          //h file in same dir
5858         FileRec *other = iter->second;
5859         //trace("local: '%s'", iname.c_str());
5860         frec->files[iname] = other;
5861         return true;
5862         }
5863     else 
5864         {
5865         //## Ok, it was not found directly
5866         //look in other dirs
5867         std::vector<String>::iterator diter;
5868         for (diter=directories.begin() ;
5869              diter!=directories.end() ; diter++)
5870             {
5871             String dfname = *diter;
5872             dfname.append("/");
5873             dfname.append(iname);
5874             URI fullPathURI(dfname);  //normalize path name
5875             String fullPath = fullPathURI.getPath();
5876             if (fullPath[0] == '/')
5877                 fullPath = fullPath.substr(1);
5878             //trace("Normalized %s to %s", dfname.c_str(), fullPath.c_str());
5879             iter = allFiles.find(fullPath);
5880             if (iter != allFiles.end())
5881                 {
5882                 FileRec *other = iter->second;
5883                 //trace("other: '%s'", iname.c_str());
5884                 frec->files[fullPath] = other;
5885                 return true;
5886                 }
5887             }
5888         }
5889     return true;
5894 /**
5895  *  Lightly parse a file to find the #include directives.  Do
5896  *  a bit of state machine stuff to make sure that the directive
5897  *  is valid.  (Like not in a comment).
5898  */
5899 bool DepTool::scanFile(const String &fname, FileRec *frec)
5901     String fileName;
5902     if (sourceDir.size() > 0)
5903         {
5904         fileName.append(sourceDir);
5905         fileName.append("/");
5906         }
5907     fileName.append(fname);
5908     String nativeName = getNativePath(fileName);
5909     FILE *f = fopen(nativeName.c_str(), "r");
5910     if (!f)
5911         {
5912         error("Could not open '%s' for reading", fname.c_str());
5913         return false;
5914         }
5915     String buf;
5916     while (!feof(f))
5917         {
5918         int nrbytes = fread(readBuf, 1, readBufSize, f);
5919         readBuf[nrbytes] = '\0';
5920         buf.append(readBuf);
5921         }
5922     fclose(f);
5924     depFileSize = buf.size();
5925     depFileBuf  = (char *)buf.c_str();
5926     int pos = 0;
5929     while (pos < depFileSize)
5930         {
5931         //trace("p:%c", get(pos));
5933         //# Block comment
5934         if (get(pos) == '/' && get(pos+1) == '*')
5935             {
5936             pos += 2;
5937             while (pos < depFileSize)
5938                 {
5939                 if (get(pos) == '*' && get(pos+1) == '/')
5940                     {
5941                     pos += 2;
5942                     break;
5943                     }
5944                 else
5945                     pos++;
5946                 }
5947             }
5948         //# Line comment
5949         else if (get(pos) == '/' && get(pos+1) == '/')
5950             {
5951             pos += 2;
5952             while (pos < depFileSize)
5953                 {
5954                 if (get(pos) == '\n')
5955                     {
5956                     pos++;
5957                     break;
5958                     }
5959                 else
5960                     pos++;
5961                 }
5962             }
5963         //# #include! yaay
5964         else if (sequ(pos, "#include"))
5965             {
5966             pos += 8;
5967             pos = skipwhite(pos);
5968             String iname;
5969             pos = getword(pos, iname);
5970             if (iname.size()>2)
5971                 {
5972                 iname = iname.substr(1, iname.size()-2);
5973                 addIncludeFile(frec, iname);
5974                 }
5975             }
5976         else
5977             {
5978             pos++;
5979             }
5980         }
5982     return true;
5987 /**
5988  *  Recursively check include lists to find all files in allFiles to which
5989  *  a given file is dependent.
5990  */
5991 bool DepTool::processDependency(FileRec *ofile, FileRec *include)
5993     std::map<String, FileRec *>::iterator iter;
5994     for (iter=include->files.begin() ; iter!=include->files.end() ; iter++)
5995         {
5996         String fname  = iter->first;
5997         if (ofile->files.find(fname) != ofile->files.end())
5998             {
5999             //trace("file '%s' already seen", fname.c_str());
6000             continue;
6001             }
6002         FileRec *child  = iter->second;
6003         ofile->files[fname] = child;
6004       
6005         processDependency(ofile, child);
6006         }
6009     return true;
6016 /**
6017  *  Generate the file dependency list.
6018  */
6019 bool DepTool::generateDependencies()
6021     std::map<String, FileRec *>::iterator iter;
6022     //# First pass.  Scan for all includes
6023     for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
6024         {
6025         FileRec *frec = iter->second;
6026         if (!scanFile(iter->first, frec))
6027             {
6028             //quit?
6029             }
6030         }
6032     //# Second pass.  Scan for all includes
6033     for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
6034         {
6035         FileRec *include = iter->second;
6036         if (include->type == FileRec::CFILE)
6037             {
6038             //String cFileName   = iter->first;
6039             FileRec *ofile     = new FileRec(FileRec::OFILE);
6040             ofile->path        = include->path;
6041             ofile->baseName    = include->baseName;
6042             ofile->suffix      = include->suffix;
6043             String fname       = include->path;
6044             if (fname.size()>0)
6045                 fname.append("/");
6046             fname.append(include->baseName);
6047             fname.append(".o");
6048             oFiles[fname]    = ofile;
6049             //add the .c file first?   no, don't
6050             //ofile->files[cFileName] = include;
6051             
6052             //trace("ofile:%s", fname.c_str());
6054             processDependency(ofile, include);
6055             }
6056         }
6058       
6059     return true;
6064 /**
6065  *  High-level call to generate deps and optionally save them
6066  */
6067 bool DepTool::generateDependencies(const String &fileName)
6069     if (!createFileList())
6070         return false;
6071     if (!generateDependencies())
6072         return false;
6073     if (!saveDepFile(fileName))
6074         return false;
6075     return true;
6079 /**
6080  *   This saves the dependency cache.
6081  */
6082 bool DepTool::saveDepFile(const String &fileName)
6084     time_t tim;
6085     time(&tim);
6087     FILE *f = fopen(fileName.c_str(), "w");
6088     if (!f)
6089         {
6090         trace("cannot open '%s' for writing", fileName.c_str());
6091         }
6092     fprintf(f, "<?xml version='1.0'?>\n");
6093     fprintf(f, "<!--\n");
6094     fprintf(f, "########################################################\n");
6095     fprintf(f, "## File: build.dep\n");
6096     fprintf(f, "## Generated by BuildTool at :%s", ctime(&tim));
6097     fprintf(f, "########################################################\n");
6098     fprintf(f, "-->\n");
6100     fprintf(f, "<dependencies source='%s'>\n\n", sourceDir.c_str());
6101     std::map<String, FileRec *>::iterator iter;
6102     for (iter=oFiles.begin() ; iter!=oFiles.end() ; iter++)
6103         {
6104         FileRec *frec = iter->second;
6105         if (frec->type == FileRec::OFILE)
6106             {
6107             fprintf(f, "<object path='%s' name='%s' suffix='%s'>\n",
6108                  frec->path.c_str(), frec->baseName.c_str(), frec->suffix.c_str());
6109             std::map<String, FileRec *>::iterator citer;
6110             for (citer=frec->files.begin() ; citer!=frec->files.end() ; citer++)
6111                 {
6112                 String cfname = citer->first;
6113                 fprintf(f, "    <dep name='%s'/>\n", cfname.c_str());
6114                 }
6115             fprintf(f, "</object>\n\n");
6116             }
6117         }
6119     fprintf(f, "</dependencies>\n");
6120     fprintf(f, "\n");
6121     fprintf(f, "<!--\n");
6122     fprintf(f, "########################################################\n");
6123     fprintf(f, "## E N D\n");
6124     fprintf(f, "########################################################\n");
6125     fprintf(f, "-->\n");
6127     fclose(f);
6129     return true;
6135 /**
6136  *   This loads the dependency cache.
6137  */
6138 std::vector<DepRec> DepTool::loadDepFile(const String &depFile)
6140     std::vector<DepRec> result;
6141     
6142     Parser parser;
6143     Element *root = parser.parseFile(depFile.c_str());
6144     if (!root)
6145         {
6146         //error("Could not open %s for reading", depFile.c_str());
6147         return result;
6148         }
6150     if (root->getChildren().size()==0 ||
6151         root->getChildren()[0]->getName()!="dependencies")
6152         {
6153         error("loadDepFile: main xml element should be <dependencies>");
6154         delete root;
6155         return result;
6156         }
6158     //########## Start parsing
6159     Element *depList = root->getChildren()[0];
6161     std::vector<Element *> objects = depList->getChildren();
6162     for (unsigned int i=0 ; i<objects.size() ; i++)
6163         {
6164         Element *objectElem = objects[i];
6165         String tagName = objectElem->getName();
6166         if (tagName != "object")
6167             {
6168             error("loadDepFile: <dependencies> should have only <object> children");
6169             return result;
6170             }
6172         String objName   = objectElem->getAttribute("name");
6173          //trace("object:%s", objName.c_str());
6174         DepRec depObject(objName);
6175         depObject.path   = objectElem->getAttribute("path");
6176         depObject.suffix = objectElem->getAttribute("suffix");
6177         //########## DESCRIPTION
6178         std::vector<Element *> depElems = objectElem->getChildren();
6179         for (unsigned int i=0 ; i<depElems.size() ; i++)
6180             {
6181             Element *depElem = depElems[i];
6182             tagName = depElem->getName();
6183             if (tagName != "dep")
6184                 {
6185                 error("loadDepFile: <object> should have only <dep> children");
6186                 return result;
6187                 }
6188             String depName = depElem->getAttribute("name");
6189             //trace("    dep:%s", depName.c_str());
6190             depObject.files.push_back(depName);
6191             }
6193         //Insert into the result list, in a sorted manner
6194         bool inserted = false;
6195         std::vector<DepRec>::iterator iter;
6196         for (iter = result.begin() ; iter != result.end() ; iter++)
6197             {
6198             String vpath = iter->path;
6199             vpath.append("/");
6200             vpath.append(iter->name);
6201             String opath = depObject.path;
6202             opath.append("/");
6203             opath.append(depObject.name);
6204             if (vpath > opath)
6205                 {
6206                 inserted = true;
6207                 iter = result.insert(iter, depObject);
6208                 break;
6209                 }
6210             }
6211         if (!inserted)
6212             result.push_back(depObject);
6213         }
6215     delete root;
6217     return result;
6221 /**
6222  *   This loads the dependency cache.
6223  */
6224 std::vector<DepRec> DepTool::getDepFile(const String &depFile,
6225                    bool forceRefresh)
6227     std::vector<DepRec> result;
6228     if (forceRefresh)
6229         {
6230         generateDependencies(depFile);
6231         result = loadDepFile(depFile);
6232         }
6233     else
6234         {
6235         //try once
6236         result = loadDepFile(depFile);
6237         if (result.size() == 0)
6238             {
6239             //fail? try again
6240             generateDependencies(depFile);
6241             result = loadDepFile(depFile);
6242             }
6243         }
6244     return result;
6250 //########################################################################
6251 //# T A S K
6252 //########################################################################
6253 //forward decl
6254 class Target;
6255 class Make;
6257 /**
6258  *
6259  */
6260 class Task : public MakeBase
6263 public:
6265     typedef enum
6266         {
6267         TASK_NONE,
6268         TASK_CC,
6269         TASK_COPY,
6270         TASK_DELETE,
6271         TASK_ECHO,
6272         TASK_JAR,
6273         TASK_JAVAC,
6274         TASK_LINK,
6275         TASK_MAKEFILE,
6276         TASK_MKDIR,
6277         TASK_MSGFMT,
6278         TASK_PKG_CONFIG,
6279         TASK_RANLIB,
6280         TASK_RC,
6281         TASK_SHAREDLIB,
6282         TASK_STATICLIB,
6283         TASK_STRIP,
6284         TASK_TOUCH,
6285         TASK_TSTAMP
6286         } TaskType;
6287         
6289     /**
6290      *
6291      */
6292     Task(MakeBase &par) : parent(par)
6293         { init(); }
6295     /**
6296      *
6297      */
6298     Task(const Task &other) : parent(other.parent)
6299         { init(); assign(other); }
6301     /**
6302      *
6303      */
6304     Task &operator=(const Task &other)
6305         { assign(other); return *this; }
6307     /**
6308      *
6309      */
6310     virtual ~Task()
6311         { }
6314     /**
6315      *
6316      */
6317     virtual MakeBase &getParent()
6318         { return parent; }
6320      /**
6321      *
6322      */
6323     virtual int  getType()
6324         { return type; }
6326     /**
6327      *
6328      */
6329     virtual void setType(int val)
6330         { type = val; }
6332     /**
6333      *
6334      */
6335     virtual String getName()
6336         { return name; }
6338     /**
6339      *
6340      */
6341     virtual bool execute()
6342         { return true; }
6344     /**
6345      *
6346      */
6347     virtual bool parse(Element *elem)
6348         { return true; }
6350     /**
6351      *
6352      */
6353     Task *createTask(Element *elem, int lineNr);
6356 protected:
6358     void init()
6359         {
6360         type = TASK_NONE;
6361         name = "none";
6362         }
6364     void assign(const Task &other)
6365         {
6366         type = other.type;
6367         name = other.name;
6368         }
6369         
6370     /**
6371      *  Show task status
6372      */
6373     void taskstatus(const char *fmt, ...)
6374         {
6375         va_list args;
6376         va_start(args,fmt);
6377         fprintf(stdout, "    %s : ", name.c_str());
6378         vfprintf(stdout, fmt, args);
6379         fprintf(stdout, "\n");
6380         va_end(args) ;
6381         }
6383     String getAttribute(Element *elem, const String &attrName)
6384         {
6385         String str;
6386         return str;
6387         }
6389     MakeBase &parent;
6391     int type;
6393     String name;
6394 };
6398 /**
6399  * This task runs the C/C++ compiler.  The compiler is invoked
6400  * for all .c or .cpp files which are newer than their correcsponding
6401  * .o files.  
6402  */
6403 class TaskCC : public Task
6405 public:
6407     TaskCC(MakeBase &par) : Task(par)
6408         {
6409         type = TASK_CC;
6410         name = "cc";
6411         }
6413     virtual ~TaskCC()
6414         {}
6415         
6416     virtual bool isExcludedInc(const String &dirname)
6417         {
6418         for (unsigned int i=0 ; i<excludeInc.size() ; i++)
6419             {
6420             String fname = excludeInc[i];
6421             if (fname == dirname)
6422                 return true;
6423             }
6424         return false;
6425         }
6427     virtual bool execute()
6428         {
6429         //evaluate our parameters
6430         String command         = parent.eval(commandOpt, "gcc");
6431         String ccCommand       = parent.eval(ccCommandOpt, "gcc");
6432         String cxxCommand      = parent.eval(cxxCommandOpt, "g++");
6433         String source          = parent.eval(sourceOpt, ".");
6434         String dest            = parent.eval(destOpt, ".");
6435         String flags           = parent.eval(flagsOpt, "");
6436         String defines         = parent.eval(definesOpt, "");
6437         String includes        = parent.eval(includesOpt, "");
6438         bool continueOnError   = parent.evalBool(continueOnErrorOpt, true);
6439         bool refreshCache      = parent.evalBool(refreshCacheOpt, false);
6441         if (!listFiles(parent, fileSet))
6442             return false;
6443             
6444         FILE *f = NULL;
6445         f = fopen("compile.lst", "w");
6447         //refreshCache is probably false here, unless specified otherwise
6448         String fullName = parent.resolve("build.dep");
6449         if (refreshCache || isNewerThan(parent.getURI().getPath(), fullName))
6450             {
6451             taskstatus("regenerating C/C++ dependency cache");
6452             refreshCache = true;
6453             }
6455         DepTool depTool;
6456         depTool.setSourceDirectory(source);
6457         depTool.setFileList(fileSet.getFiles());
6458         std::vector<DepRec> deps =
6459              depTool.getDepFile("build.dep", refreshCache);
6460         
6461         String incs;
6462         incs.append("-I");
6463         incs.append(parent.resolve("."));
6464         incs.append(" ");
6465         if (includes.size()>0)
6466             {
6467             incs.append(includes);
6468             incs.append(" ");
6469             }
6470         std::set<String> paths;
6471         std::vector<DepRec>::iterator viter;
6472         for (viter=deps.begin() ; viter!=deps.end() ; viter++)
6473             {
6474             DepRec dep = *viter;
6475             if (dep.path.size()>0)
6476                 paths.insert(dep.path);
6477             }
6478         if (source.size()>0)
6479             {
6480             incs.append(" -I");
6481             incs.append(parent.resolve(source));
6482             incs.append(" ");
6483             }
6484         std::set<String>::iterator setIter;
6485         for (setIter=paths.begin() ; setIter!=paths.end() ; setIter++)
6486             {
6487             String dirName = *setIter;
6488             //check excludeInc to see if we dont want to include this dir
6489             if (isExcludedInc(dirName))
6490                 continue;
6491             incs.append(" -I");
6492             String dname;
6493             if (source.size()>0)
6494                 {
6495                 dname.append(source);
6496                 dname.append("/");
6497                 }
6498             dname.append(dirName);
6499             incs.append(parent.resolve(dname));
6500             }
6501             
6502         /**
6503          * Compile each of the C files that need it
6504          */
6505         bool errorOccurred = false;                 
6506         std::vector<String> cfiles;
6507         for (viter=deps.begin() ; viter!=deps.end() ; viter++)
6508             {
6509             DepRec dep = *viter;
6511             //## Select command
6512             String sfx = dep.suffix;
6513             String command = ccCommand;
6514             if (sfx == "cpp" || sfx == "cxx" || sfx == "c++" ||
6515                  sfx == "cc" || sfx == "CC")
6516                 command = cxxCommand;
6517  
6518             //## Make paths
6519             String destPath = dest;
6520             String srcPath  = source;
6521             if (dep.path.size()>0)
6522                 {
6523                 destPath.append("/");
6524                 destPath.append(dep.path);
6525                 srcPath.append("/");
6526                 srcPath.append(dep.path);
6527                 }
6528             //## Make sure destination directory exists
6529             if (!createDirectory(destPath))
6530                 return false;
6531                 
6532             //## Check whether it needs to be done
6533             String destName;
6534             if (destPath.size()>0)
6535                 {
6536                 destName.append(destPath);
6537                 destName.append("/");
6538                 }
6539             destName.append(dep.name);
6540             destName.append(".o");
6541             String destFullName = parent.resolve(destName);
6542             String srcName;
6543             if (srcPath.size()>0)
6544                 {
6545                 srcName.append(srcPath);
6546                 srcName.append("/");
6547                 }
6548             srcName.append(dep.name);
6549             srcName.append(".");
6550             srcName.append(dep.suffix);
6551             String srcFullName = parent.resolve(srcName);
6552             bool compileMe = false;
6553             //# First we check if the source is newer than the .o
6554             if (isNewerThan(srcFullName, destFullName))
6555                 {
6556                 taskstatus("compile of %s required by source: %s",
6557                         destFullName.c_str(), srcFullName.c_str());
6558                 compileMe = true;
6559                 }
6560             else
6561                 {
6562                 //# secondly, we check if any of the included dependencies
6563                 //# of the .c/.cpp is newer than the .o
6564                 for (unsigned int i=0 ; i<dep.files.size() ; i++)
6565                     {
6566                     String depName;
6567                     if (source.size()>0)
6568                         {
6569                         depName.append(source);
6570                         depName.append("/");
6571                         }
6572                     depName.append(dep.files[i]);
6573                     String depFullName = parent.resolve(depName);
6574                     bool depRequires = isNewerThan(depFullName, destFullName);
6575                     //trace("%d %s %s\n", depRequires,
6576                     //        destFullName.c_str(), depFullName.c_str());
6577                     if (depRequires)
6578                         {
6579                         taskstatus("compile of %s required by included: %s",
6580                                 destFullName.c_str(), depFullName.c_str());
6581                         compileMe = true;
6582                         break;
6583                         }
6584                     }
6585                 }
6586             if (!compileMe)
6587                 {
6588                 continue;
6589                 }
6591             //## Assemble the command
6592             String cmd = command;
6593             cmd.append(" -c ");
6594             cmd.append(flags);
6595             cmd.append(" ");
6596             cmd.append(defines);
6597             cmd.append(" ");
6598             cmd.append(incs);
6599             cmd.append(" ");
6600             cmd.append(srcFullName);
6601             cmd.append(" -o ");
6602             cmd.append(destFullName);
6604             //## Execute the command
6606             String outString, errString;
6607             bool ret = executeCommand(cmd.c_str(), "", outString, errString);
6609             if (f)
6610                 {
6611                 fprintf(f, "########################### File : %s\n",
6612                              srcFullName.c_str());
6613                 fprintf(f, "#### COMMAND ###\n");
6614                 int col = 0;
6615                 for (unsigned int i = 0 ; i < cmd.size() ; i++)
6616                     {
6617                     char ch = cmd[i];
6618                     if (isspace(ch)  && col > 63)
6619                         {
6620                         fputc('\n', f);
6621                         col = 0;
6622                         }
6623                     else
6624                         {
6625                         fputc(ch, f);
6626                         col++;
6627                         }
6628                     if (col > 76)
6629                         {
6630                         fputc('\n', f);
6631                         col = 0;
6632                         }
6633                     }
6634                 fprintf(f, "\n");
6635                 fprintf(f, "#### STDOUT ###\n%s\n", outString.c_str());
6636                 fprintf(f, "#### STDERR ###\n%s\n\n", errString.c_str());
6637                 fflush(f);
6638                 }
6639             if (!ret)
6640                 {
6641                 error("problem compiling: %s", errString.c_str());
6642                 errorOccurred = true;
6643                 }
6644             if (errorOccurred && !continueOnError)
6645                 break;
6646             }
6648         if (f)
6649             {
6650             fclose(f);
6651             }
6652         
6653         return !errorOccurred;
6654         }
6657     virtual bool parse(Element *elem)
6658         {
6659         String s;
6660         if (!parent.getAttribute(elem, "command", commandOpt))
6661             return false;
6662         if (commandOpt.size()>0)
6663             { cxxCommandOpt = ccCommandOpt = commandOpt; }
6664         if (!parent.getAttribute(elem, "cc", ccCommandOpt))
6665             return false;
6666         if (!parent.getAttribute(elem, "cxx", cxxCommandOpt))
6667             return false;
6668         if (!parent.getAttribute(elem, "destdir", destOpt))
6669             return false;
6670         if (!parent.getAttribute(elem, "continueOnError", continueOnErrorOpt))
6671             return false;
6672         if (!parent.getAttribute(elem, "refreshCache", refreshCacheOpt))
6673             return false;
6675         std::vector<Element *> children = elem->getChildren();
6676         for (unsigned int i=0 ; i<children.size() ; i++)
6677             {
6678             Element *child = children[i];
6679             String tagName = child->getName();
6680             if (tagName == "flags")
6681                 {
6682                 if (!parent.getValue(child, flagsOpt))
6683                     return false;
6684                 flagsOpt = strip(flagsOpt);
6685                 }
6686             else if (tagName == "includes")
6687                 {
6688                 if (!parent.getValue(child, includesOpt))
6689                     return false;
6690                 includesOpt = strip(includesOpt);
6691                 }
6692             else if (tagName == "defines")
6693                 {
6694                 if (!parent.getValue(child, definesOpt))
6695                     return false;
6696                 definesOpt = strip(definesOpt);
6697                 }
6698             else if (tagName == "fileset")
6699                 {
6700                 if (!parseFileSet(child, parent, fileSet))
6701                     return false;
6702                 sourceOpt = fileSet.getDirectory();
6703                 }
6704             else if (tagName == "excludeinc")
6705                 {
6706                 if (!parseFileList(child, parent, excludeInc))
6707                     return false;
6708                 }
6709             }
6711         return true;
6712         }
6713         
6714 protected:
6716     String   commandOpt;
6717     String   ccCommandOpt;
6718     String   cxxCommandOpt;
6719     String   sourceOpt;
6720     String   destOpt;
6721     String   flagsOpt;
6722     String   definesOpt;
6723     String   includesOpt;
6724     String   continueOnErrorOpt;
6725     String   refreshCacheOpt;
6726     FileSet  fileSet;
6727     FileList excludeInc;
6728     
6729 };
6733 /**
6734  *
6735  */
6736 class TaskCopy : public Task
6738 public:
6740     typedef enum
6741         {
6742         CP_NONE,
6743         CP_TOFILE,
6744         CP_TODIR
6745         } CopyType;
6747     TaskCopy(MakeBase &par) : Task(par)
6748         {
6749         type        = TASK_COPY;
6750         name        = "copy";
6751         cptype      = CP_NONE;
6752         haveFileSet = false;
6753         }
6755     virtual ~TaskCopy()
6756         {}
6758     virtual bool execute()
6759         {
6760         String fileName   = parent.eval(fileNameOpt   , ".");
6761         String toFileName = parent.eval(toFileNameOpt , ".");
6762         String toDirName  = parent.eval(toDirNameOpt  , ".");
6763         bool   verbose    = parent.evalBool(verboseOpt, false);
6764         switch (cptype)
6765            {
6766            case CP_TOFILE:
6767                {
6768                if (fileName.size()>0)
6769                    {
6770                    taskstatus("%s to %s",
6771                         fileName.c_str(), toFileName.c_str());
6772                    String fullSource = parent.resolve(fileName);
6773                    String fullDest = parent.resolve(toFileName);
6774                    if (verbose)
6775                        taskstatus("copy %s to file %s", fullSource.c_str(),
6776                                           fullDest.c_str());
6777                    if (!isRegularFile(fullSource))
6778                        {
6779                        error("copy : file %s does not exist", fullSource.c_str());
6780                        return false;
6781                        }
6782                    if (!isNewerThan(fullSource, fullDest))
6783                        {
6784                        taskstatus("skipped");
6785                        return true;
6786                        }
6787                    if (!copyFile(fullSource, fullDest))
6788                        return false;
6789                    taskstatus("1 file copied");
6790                    }
6791                return true;
6792                }
6793            case CP_TODIR:
6794                {
6795                if (haveFileSet)
6796                    {
6797                    if (!listFiles(parent, fileSet))
6798                        return false;
6799                    String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
6801                    taskstatus("%s to %s",
6802                        fileSetDir.c_str(), toDirName.c_str());
6804                    int nrFiles = 0;
6805                    for (unsigned int i=0 ; i<fileSet.size() ; i++)
6806                        {
6807                        String fileName = fileSet[i];
6809                        String sourcePath;
6810                        if (fileSetDir.size()>0)
6811                            {
6812                            sourcePath.append(fileSetDir);
6813                            sourcePath.append("/");
6814                            }
6815                        sourcePath.append(fileName);
6816                        String fullSource = parent.resolve(sourcePath);
6817                        
6818                        //Get the immediate parent directory's base name
6819                        String baseFileSetDir = fileSetDir;
6820                        unsigned int pos = baseFileSetDir.find_last_of('/');
6821                        if (pos!=baseFileSetDir.npos &&
6822                                   pos < baseFileSetDir.size()-1)
6823                            baseFileSetDir =
6824                               baseFileSetDir.substr(pos+1,
6825                                    baseFileSetDir.size());
6826                        //Now make the new path
6827                        String destPath;
6828                        if (toDirName.size()>0)
6829                            {
6830                            destPath.append(toDirName);
6831                            destPath.append("/");
6832                            }
6833                        if (baseFileSetDir.size()>0)
6834                            {
6835                            destPath.append(baseFileSetDir);
6836                            destPath.append("/");
6837                            }
6838                        destPath.append(fileName);
6839                        String fullDest = parent.resolve(destPath);
6840                        //trace("fileName:%s", fileName.c_str());
6841                        if (verbose)
6842                            taskstatus("copy %s to new dir : %s",
6843                                  fullSource.c_str(), fullDest.c_str());
6844                        if (!isNewerThan(fullSource, fullDest))
6845                            {
6846                            if (verbose)
6847                                taskstatus("copy skipping %s", fullSource.c_str());
6848                            continue;
6849                            }
6850                        if (!copyFile(fullSource, fullDest))
6851                            return false;
6852                        nrFiles++;
6853                        }
6854                    taskstatus("%d file(s) copied", nrFiles);
6855                    }
6856                else //file source
6857                    {
6858                    //For file->dir we want only the basename of
6859                    //the source appended to the dest dir
6860                    taskstatus("%s to %s", 
6861                        fileName.c_str(), toDirName.c_str());
6862                    String baseName = fileName;
6863                    unsigned int pos = baseName.find_last_of('/');
6864                    if (pos!=baseName.npos && pos<baseName.size()-1)
6865                        baseName = baseName.substr(pos+1, baseName.size());
6866                    String fullSource = parent.resolve(fileName);
6867                    String destPath;
6868                    if (toDirName.size()>0)
6869                        {
6870                        destPath.append(toDirName);
6871                        destPath.append("/");
6872                        }
6873                    destPath.append(baseName);
6874                    String fullDest = parent.resolve(destPath);
6875                    if (verbose)
6876                        taskstatus("file %s to new dir : %s", fullSource.c_str(),
6877                                           fullDest.c_str());
6878                    if (!isRegularFile(fullSource))
6879                        {
6880                        error("copy : file %s does not exist", fullSource.c_str());
6881                        return false;
6882                        }
6883                    if (!isNewerThan(fullSource, fullDest))
6884                        {
6885                        taskstatus("skipped");
6886                        return true;
6887                        }
6888                    if (!copyFile(fullSource, fullDest))
6889                        return false;
6890                    taskstatus("1 file copied");
6891                    }
6892                return true;
6893                }
6894            }
6895         return true;
6896         }
6899     virtual bool parse(Element *elem)
6900         {
6901         if (!parent.getAttribute(elem, "file", fileNameOpt))
6902             return false;
6903         if (!parent.getAttribute(elem, "tofile", toFileNameOpt))
6904             return false;
6905         if (toFileNameOpt.size() > 0)
6906             cptype = CP_TOFILE;
6907         if (!parent.getAttribute(elem, "todir", toDirNameOpt))
6908             return false;
6909         if (toDirNameOpt.size() > 0)
6910             cptype = CP_TODIR;
6911         if (!parent.getAttribute(elem, "verbose", verboseOpt))
6912             return false;
6913             
6914         haveFileSet = false;
6915         
6916         std::vector<Element *> children = elem->getChildren();
6917         for (unsigned int i=0 ; i<children.size() ; i++)
6918             {
6919             Element *child = children[i];
6920             String tagName = child->getName();
6921             if (tagName == "fileset")
6922                 {
6923                 if (!parseFileSet(child, parent, fileSet))
6924                     {
6925                     error("problem getting fileset");
6926                     return false;
6927                     }
6928                 haveFileSet = true;
6929                 }
6930             }
6932         //Perform validity checks
6933         if (fileNameOpt.size()>0 && fileSet.size()>0)
6934             {
6935             error("<copy> can only have one of : file= and <fileset>");
6936             return false;
6937             }
6938         if (toFileNameOpt.size()>0 && toDirNameOpt.size()>0)
6939             {
6940             error("<copy> can only have one of : tofile= or todir=");
6941             return false;
6942             }
6943         if (haveFileSet && toDirNameOpt.size()==0)
6944             {
6945             error("a <copy> task with a <fileset> must have : todir=");
6946             return false;
6947             }
6948         if (cptype == CP_TOFILE && fileNameOpt.size()==0)
6949             {
6950             error("<copy> tofile= must be associated with : file=");
6951             return false;
6952             }
6953         if (cptype == CP_TODIR && fileNameOpt.size()==0 && !haveFileSet)
6954             {
6955             error("<copy> todir= must be associated with : file= or <fileset>");
6956             return false;
6957             }
6959         return true;
6960         }
6961         
6962 private:
6964     int cptype;
6965     bool haveFileSet;
6967     FileSet fileSet;
6968     String  fileNameOpt;
6969     String  toFileNameOpt;
6970     String  toDirNameOpt;
6971     String  verboseOpt;
6972 };
6975 /**
6976  *
6977  */
6978 class TaskDelete : public Task
6980 public:
6982     typedef enum
6983         {
6984         DEL_FILE,
6985         DEL_DIR,
6986         DEL_FILESET
6987         } DeleteType;
6989     TaskDelete(MakeBase &par) : Task(par)
6990         { 
6991         type        = TASK_DELETE;
6992         name        = "delete";
6993         delType     = DEL_FILE;
6994         }
6996     virtual ~TaskDelete()
6997         {}
6999     virtual bool execute()
7000         {
7001         String dirName   = parent.eval(dirNameOpt, ".");
7002         String fileName  = parent.eval(fileNameOpt, ".");
7003         bool verbose     = parent.evalBool(verboseOpt, false);
7004         bool quiet       = parent.evalBool(quietOpt, false);
7005         bool failOnError = parent.evalBool(failOnErrorOpt, true);
7006         struct stat finfo;
7007         switch (delType)
7008             {
7009             case DEL_FILE:
7010                 {
7011                 taskstatus("file: %s", fileName.c_str());
7012                 String fullName = parent.resolve(fileName);
7013                 char *fname = (char *)fullName.c_str();
7014                 if (!quiet && verbose)
7015                     taskstatus("path: %s", fname);
7016                 //does not exist
7017                 if (stat(fname, &finfo)<0)
7018                     {
7019                     if (failOnError)
7020                         return false;
7021                     else
7022                         return true;
7023                     }
7024                 //exists but is not a regular file
7025                 if (!S_ISREG(finfo.st_mode))
7026                     {
7027                     error("<delete> failed. '%s' exists and is not a regular file",
7028                           fname);
7029                     return false;
7030                     }
7031                 if (remove(fname)<0)
7032                     {
7033                     error("<delete> failed: %s", strerror(errno));
7034                     return false;
7035                     }
7036                 return true;
7037                 }
7038             case DEL_DIR:
7039                 {
7040                 taskstatus("dir: %s", dirName.c_str());
7041                 String fullDir = parent.resolve(dirName);
7042                 if (!quiet && verbose)
7043                     taskstatus("path: %s", fullDir.c_str());
7044                 if (!removeDirectory(fullDir))
7045                     return false;
7046                 return true;
7047                 }
7048             }
7049         return true;
7050         }
7052     virtual bool parse(Element *elem)
7053         {
7054         if (!parent.getAttribute(elem, "file", fileNameOpt))
7055             return false;
7056         if (fileNameOpt.size() > 0)
7057             delType = DEL_FILE;
7058         if (!parent.getAttribute(elem, "dir", dirNameOpt))
7059             return false;
7060         if (dirNameOpt.size() > 0)
7061             delType = DEL_DIR;
7062         if (fileNameOpt.size()>0 && dirNameOpt.size()>0)
7063             {
7064             error("<delete> can have one attribute of file= or dir=");
7065             return false;
7066             }
7067         if (fileNameOpt.size()==0 && dirNameOpt.size()==0)
7068             {
7069             error("<delete> must have one attribute of file= or dir=");
7070             return false;
7071             }
7072         if (!parent.getAttribute(elem, "verbose", verboseOpt))
7073             return false;
7074         if (!parent.getAttribute(elem, "quiet", quietOpt))
7075             return false;
7076         if (!parent.getAttribute(elem, "failonerror", failOnErrorOpt))
7077             return false;
7078         return true;
7079         }
7081 private:
7083     int delType;
7084     String dirNameOpt;
7085     String fileNameOpt;
7086     String verboseOpt;
7087     String quietOpt;
7088     String failOnErrorOpt;
7089 };
7092 /**
7093  * Send a message to stdout
7094  */
7095 class TaskEcho : public Task
7097 public:
7099     TaskEcho(MakeBase &par) : Task(par)
7100         { type = TASK_ECHO; name = "echo"; }
7102     virtual ~TaskEcho()
7103         {}
7105     virtual bool execute()
7106         {
7107         //let message have priority over text
7108         String message = parent.eval(messageOpt, "");
7109         String text    = parent.eval(textOpt, "");
7110         if (message.size() > 0)
7111             {
7112             fprintf(stdout, "%s\n", message.c_str());
7113             }
7114         else if (text.size() > 0)
7115             {
7116             fprintf(stdout, "%s\n", text.c_str());
7117             }
7118         return true;
7119         }
7121     virtual bool parse(Element *elem)
7122         {
7123         if (!parent.getValue(elem, textOpt))
7124             return false;
7125         textOpt    = leftJustify(textOpt);
7126         if (!parent.getAttribute(elem, "message", messageOpt))
7127             return false;
7128         return true;
7129         }
7131 private:
7133     String messageOpt;
7134     String textOpt;
7135 };
7139 /**
7140  *
7141  */
7142 class TaskJar : public Task
7144 public:
7146     TaskJar(MakeBase &par) : Task(par)
7147         { type = TASK_JAR; name = "jar"; }
7149     virtual ~TaskJar()
7150         {}
7152     virtual bool execute()
7153         {
7154         String command  = parent.eval(commandOpt, "jar");
7155         String basedir  = parent.eval(basedirOpt, ".");
7156         String destfile = parent.eval(destfileOpt, ".");
7158         String cmd = command;
7159         cmd.append(" -cf ");
7160         cmd.append(destfile);
7161         cmd.append(" -C ");
7162         cmd.append(basedir);
7163         cmd.append(" .");
7165         String execCmd = cmd;
7167         String outString, errString;
7168         bool ret = executeCommand(execCmd.c_str(), "", outString, errString);
7169         if (!ret)
7170             {
7171             error("<jar> command '%s' failed :\n %s",
7172                                       execCmd.c_str(), errString.c_str());
7173             return false;
7174             }
7175         return true;
7176         }
7178     virtual bool parse(Element *elem)
7179         {
7180         if (!parent.getAttribute(elem, "command", commandOpt))
7181             return false;
7182         if (!parent.getAttribute(elem, "basedir", basedirOpt))
7183             return false;
7184         if (!parent.getAttribute(elem, "destfile", destfileOpt))
7185             return false;
7186         if (basedirOpt.size() == 0 || destfileOpt.size() == 0)
7187             {
7188             error("<jar> required both basedir and destfile attributes to be set");
7189             return false;
7190             }
7191         return true;
7192         }
7194 private:
7196     String commandOpt;
7197     String basedirOpt;
7198     String destfileOpt;
7199 };
7202 /**
7203  *
7204  */
7205 class TaskJavac : public Task
7207 public:
7209     TaskJavac(MakeBase &par) : Task(par)
7210         { 
7211         type = TASK_JAVAC; name = "javac";
7212         }
7214     virtual ~TaskJavac()
7215         {}
7217     virtual bool execute()
7218         {
7219         String command  = parent.eval(commandOpt, "javac");
7220         String srcdir   = parent.eval(srcdirOpt, ".");
7221         String destdir  = parent.eval(destdirOpt, ".");
7222         String target   = parent.eval(targetOpt, "");
7224         std::vector<String> fileList;
7225         if (!listFiles(srcdir, "", fileList))
7226             {
7227             return false;
7228             }
7229         String cmd = command;
7230         cmd.append(" -d ");
7231         cmd.append(destdir);
7232         cmd.append(" -classpath ");
7233         cmd.append(destdir);
7234         cmd.append(" -sourcepath ");
7235         cmd.append(srcdir);
7236         cmd.append(" ");
7237         if (target.size()>0)
7238             {
7239             cmd.append(" -target ");
7240             cmd.append(target);
7241             cmd.append(" ");
7242             }
7243         String fname = "javalist.btool";
7244         FILE *f = fopen(fname.c_str(), "w");
7245         int count = 0;
7246         for (unsigned int i=0 ; i<fileList.size() ; i++)
7247             {
7248             String fname = fileList[i];
7249             String srcName = fname;
7250             if (fname.size()<6) //x.java
7251                 continue;
7252             if (fname.compare(fname.size()-5, 5, ".java") != 0)
7253                 continue;
7254             String baseName = fname.substr(0, fname.size()-5);
7255             String destName = baseName;
7256             destName.append(".class");
7258             String fullSrc = srcdir;
7259             fullSrc.append("/");
7260             fullSrc.append(fname);
7261             String fullDest = destdir;
7262             fullDest.append("/");
7263             fullDest.append(destName);
7264             //trace("fullsrc:%s fulldest:%s", fullSrc.c_str(), fullDest.c_str());
7265             if (!isNewerThan(fullSrc, fullDest))
7266                 continue;
7268             count++;
7269             fprintf(f, "%s\n", fullSrc.c_str());
7270             }
7271         fclose(f);
7272         if (!count)
7273             {
7274             taskstatus("nothing to do");
7275             return true;
7276             }
7278         taskstatus("compiling %d files", count);
7280         String execCmd = cmd;
7281         execCmd.append("@");
7282         execCmd.append(fname);
7284         String outString, errString;
7285         bool ret = executeCommand(execCmd.c_str(), "", outString, errString);
7286         if (!ret)
7287             {
7288             error("<javac> command '%s' failed :\n %s",
7289                                       execCmd.c_str(), errString.c_str());
7290             return false;
7291             }
7292         return true;
7293         }
7295     virtual bool parse(Element *elem)
7296         {
7297         if (!parent.getAttribute(elem, "command", commandOpt))
7298             return false;
7299         if (!parent.getAttribute(elem, "srcdir", srcdirOpt))
7300             return false;
7301         if (!parent.getAttribute(elem, "destdir", destdirOpt))
7302             return false;
7303         if (srcdirOpt.size() == 0 || destdirOpt.size() == 0)
7304             {
7305             error("<javac> required both srcdir and destdir attributes to be set");
7306             return false;
7307             }
7308         if (!parent.getAttribute(elem, "target", targetOpt))
7309             return false;
7310         return true;
7311         }
7313 private:
7315     String commandOpt;
7316     String srcdirOpt;
7317     String destdirOpt;
7318     String targetOpt;
7320 };
7323 /**
7324  *
7325  */
7326 class TaskLink : public Task
7328 public:
7330     TaskLink(MakeBase &par) : Task(par)
7331         {
7332         type = TASK_LINK; name = "link";
7333         }
7335     virtual ~TaskLink()
7336         {}
7338     virtual bool execute()
7339         {
7340         String  command        = parent.eval(commandOpt, "g++");
7341         String  fileName       = parent.eval(fileNameOpt, "");
7342         String  flags          = parent.eval(flagsOpt, "");
7343         String  libs           = parent.eval(libsOpt, "");
7344         bool    doStrip        = parent.evalBool(doStripOpt, false);
7345         String  symFileName    = parent.eval(symFileNameOpt, "");
7346         String  stripCommand   = parent.eval(stripCommandOpt, "strip");
7347         String  objcopyCommand = parent.eval(objcopyCommandOpt, "objcopy");
7349         if (!listFiles(parent, fileSet))
7350             return false;
7351         String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
7352         //trace("%d files in %s", fileSet.size(), fileSetDir.c_str());
7353         bool doit = false;
7354         String fullTarget = parent.resolve(fileName);
7355         String cmd = command;
7356         cmd.append(" -o ");
7357         cmd.append(fullTarget);
7358         cmd.append(" ");
7359         cmd.append(flags);
7360         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7361             {
7362             cmd.append(" ");
7363             String obj;
7364             if (fileSetDir.size()>0)
7365                 {
7366                 obj.append(fileSetDir);
7367                 obj.append("/");
7368                 }
7369             obj.append(fileSet[i]);
7370             String fullObj = parent.resolve(obj);
7371             String nativeFullObj = getNativePath(fullObj);
7372             cmd.append(nativeFullObj);
7373             //trace("link: tgt:%s obj:%s", fullTarget.c_str(),
7374             //          fullObj.c_str());
7375             if (isNewerThan(fullObj, fullTarget))
7376                 doit = true;
7377             }
7378         cmd.append(" ");
7379         cmd.append(libs);
7380         if (!doit)
7381             {
7382             //trace("link not needed");
7383             return true;
7384             }
7385         //trace("LINK cmd:%s", cmd.c_str());
7388         String outbuf, errbuf;
7389         if (!executeCommand(cmd.c_str(), "", outbuf, errbuf))
7390             {
7391             error("LINK problem: %s", errbuf.c_str());
7392             return false;
7393             }
7395         if (symFileName.size()>0)
7396             {
7397             String symFullName = parent.resolve(symFileName);
7398             cmd = objcopyCommand;
7399             cmd.append(" --only-keep-debug ");
7400             cmd.append(getNativePath(fullTarget));
7401             cmd.append(" ");
7402             cmd.append(getNativePath(symFullName));
7403             if (!executeCommand(cmd, "", outbuf, errbuf))
7404                 {
7405                 error("<strip> symbol file failed : %s", errbuf.c_str());
7406                 return false;
7407                 }
7408             }
7409             
7410         if (doStrip)
7411             {
7412             cmd = stripCommand;
7413             cmd.append(" ");
7414             cmd.append(getNativePath(fullTarget));
7415             if (!executeCommand(cmd, "", outbuf, errbuf))
7416                {
7417                error("<strip> failed : %s", errbuf.c_str());
7418                return false;
7419                }
7420             }
7422         return true;
7423         }
7425     virtual bool parse(Element *elem)
7426         {
7427         if (!parent.getAttribute(elem, "command", commandOpt))
7428             return false;
7429         if (!parent.getAttribute(elem, "objcopycommand", objcopyCommandOpt))
7430             return false;
7431         if (!parent.getAttribute(elem, "stripcommand", stripCommandOpt))
7432             return false;
7433         if (!parent.getAttribute(elem, "out", fileNameOpt))
7434             return false;
7435         if (!parent.getAttribute(elem, "strip", doStripOpt))
7436             return false;
7437         if (!parent.getAttribute(elem, "symfile", symFileNameOpt))
7438             return false;
7439             
7440         std::vector<Element *> children = elem->getChildren();
7441         for (unsigned int i=0 ; i<children.size() ; i++)
7442             {
7443             Element *child = children[i];
7444             String tagName = child->getName();
7445             if (tagName == "fileset")
7446                 {
7447                 if (!parseFileSet(child, parent, fileSet))
7448                     return false;
7449                 }
7450             else if (tagName == "flags")
7451                 {
7452                 if (!parent.getValue(child, flagsOpt))
7453                     return false;
7454                 flagsOpt = strip(flagsOpt);
7455                 }
7456             else if (tagName == "libs")
7457                 {
7458                 if (!parent.getValue(child, libsOpt))
7459                     return false;
7460                 libsOpt = strip(libsOpt);
7461                 }
7462             }
7463         return true;
7464         }
7466 private:
7468     FileSet fileSet;
7470     String  commandOpt;
7471     String  fileNameOpt;
7472     String  flagsOpt;
7473     String  libsOpt;
7474     String  doStripOpt;
7475     String  symFileNameOpt;
7476     String  stripCommandOpt;
7477     String  objcopyCommandOpt;
7479 };
7483 /**
7484  * Create a named file
7485  */
7486 class TaskMakeFile : public Task
7488 public:
7490     TaskMakeFile(MakeBase &par) : Task(par)
7491         { type = TASK_MAKEFILE; name = "makefile"; }
7493     virtual ~TaskMakeFile()
7494         {}
7496     virtual bool execute()
7497         {
7498         String fileName = parent.eval(fileNameOpt, "");
7499         String text     = parent.eval(textOpt, "");
7501         taskstatus("%s", fileName.c_str());
7502         String fullName = parent.resolve(fileName);
7503         if (!isNewerThan(parent.getURI().getPath(), fullName))
7504             {
7505             //trace("skipped <makefile>");
7506             return true;
7507             }
7508         String fullNative = getNativePath(fullName);
7509         //trace("fullName:%s", fullName.c_str());
7510         FILE *f = fopen(fullNative.c_str(), "w");
7511         if (!f)
7512             {
7513             error("<makefile> could not open %s for writing : %s",
7514                 fullName.c_str(), strerror(errno));
7515             return false;
7516             }
7517         for (unsigned int i=0 ; i<text.size() ; i++)
7518             fputc(text[i], f);
7519         fputc('\n', f);
7520         fclose(f);
7521         return true;
7522         }
7524     virtual bool parse(Element *elem)
7525         {
7526         if (!parent.getAttribute(elem, "file", fileNameOpt))
7527             return false;
7528         if (fileNameOpt.size() == 0)
7529             {
7530             error("<makefile> requires 'file=\"filename\"' attribute");
7531             return false;
7532             }
7533         if (!parent.getValue(elem, textOpt))
7534             return false;
7535         textOpt = leftJustify(textOpt);
7536         //trace("dirname:%s", dirName.c_str());
7537         return true;
7538         }
7540 private:
7542     String fileNameOpt;
7543     String textOpt;
7544 };
7548 /**
7549  * Create a named directory
7550  */
7551 class TaskMkDir : public Task
7553 public:
7555     TaskMkDir(MakeBase &par) : Task(par)
7556         { type = TASK_MKDIR; name = "mkdir"; }
7558     virtual ~TaskMkDir()
7559         {}
7561     virtual bool execute()
7562         {
7563         String dirName = parent.eval(dirNameOpt, ".");
7564         
7565         taskstatus("%s", dirName.c_str());
7566         String fullDir = parent.resolve(dirName);
7567         //trace("fullDir:%s", fullDir.c_str());
7568         if (!createDirectory(fullDir))
7569             return false;
7570         return true;
7571         }
7573     virtual bool parse(Element *elem)
7574         {
7575         if (!parent.getAttribute(elem, "dir", dirNameOpt))
7576             return false;
7577         if (dirNameOpt.size() == 0)
7578             {
7579             error("<mkdir> requires 'dir=\"dirname\"' attribute");
7580             return false;
7581             }
7582         return true;
7583         }
7585 private:
7587     String dirNameOpt;
7588 };
7592 /**
7593  * Create a named directory
7594  */
7595 class TaskMsgFmt: public Task
7597 public:
7599     TaskMsgFmt(MakeBase &par) : Task(par)
7600          { type = TASK_MSGFMT;  name = "msgfmt"; }
7602     virtual ~TaskMsgFmt()
7603         {}
7605     virtual bool execute()
7606         {
7607         String  command   = parent.eval(commandOpt, "msgfmt");
7608         String  toDirName = parent.eval(toDirNameOpt, ".");
7609         String  outName   = parent.eval(outNameOpt, "");
7610         bool    owndir    = parent.evalBool(owndirOpt, false);
7612         if (!listFiles(parent, fileSet))
7613             return false;
7614         String fileSetDir = fileSet.getDirectory();
7616         //trace("msgfmt: %d", fileSet.size());
7617         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7618             {
7619             String fileName = fileSet[i];
7620             if (getSuffix(fileName) != "po")
7621                 continue;
7622             String sourcePath;
7623             if (fileSetDir.size()>0)
7624                 {
7625                 sourcePath.append(fileSetDir);
7626                 sourcePath.append("/");
7627                 }
7628             sourcePath.append(fileName);
7629             String fullSource = parent.resolve(sourcePath);
7631             String destPath;
7632             if (toDirName.size()>0)
7633                 {
7634                 destPath.append(toDirName);
7635                 destPath.append("/");
7636                 }
7637             if (owndir)
7638                 {
7639                 String subdir = fileName;
7640                 unsigned int pos = subdir.find_last_of('.');
7641                 if (pos != subdir.npos)
7642                     subdir = subdir.substr(0, pos);
7643                 destPath.append(subdir);
7644                 destPath.append("/");
7645                 }
7646             //Pick the output file name
7647             if (outName.size() > 0)
7648                 {
7649                 destPath.append(outName);
7650                 }
7651             else
7652                 {
7653                 destPath.append(fileName);
7654                 destPath[destPath.size()-2] = 'm';
7655                 }
7657             String fullDest = parent.resolve(destPath);
7659             if (!isNewerThan(fullSource, fullDest))
7660                 {
7661                 //trace("skip %s", fullSource.c_str());
7662                 continue;
7663                 }
7664                 
7665             String cmd = command;
7666             cmd.append(" ");
7667             cmd.append(fullSource);
7668             cmd.append(" -o ");
7669             cmd.append(fullDest);
7670             
7671             int pos = fullDest.find_last_of('/');
7672             if (pos>0)
7673                 {
7674                 String fullDestPath = fullDest.substr(0, pos);
7675                 if (!createDirectory(fullDestPath))
7676                     return false;
7677                 }
7681             String outString, errString;
7682             if (!executeCommand(cmd.c_str(), "", outString, errString))
7683                 {
7684                 error("<msgfmt> problem: %s", errString.c_str());
7685                 return false;
7686                 }
7687             }
7689         return true;
7690         }
7692     virtual bool parse(Element *elem)
7693         {
7694         if (!parent.getAttribute(elem, "command", commandOpt))
7695             return false;
7696         if (!parent.getAttribute(elem, "todir", toDirNameOpt))
7697             return false;
7698         if (!parent.getAttribute(elem, "out", outNameOpt))
7699             return false;
7700         if (!parent.getAttribute(elem, "owndir", owndirOpt))
7701             return false;
7702             
7703         std::vector<Element *> children = elem->getChildren();
7704         for (unsigned int i=0 ; i<children.size() ; i++)
7705             {
7706             Element *child = children[i];
7707             String tagName = child->getName();
7708             if (tagName == "fileset")
7709                 {
7710                 if (!parseFileSet(child, parent, fileSet))
7711                     return false;
7712                 }
7713             }
7714         return true;
7715         }
7717 private:
7719     FileSet fileSet;
7721     String  commandOpt;
7722     String  toDirNameOpt;
7723     String  outNameOpt;
7724     String  owndirOpt;
7726 };
7730 /**
7731  *  Perform a Package-Config query similar to pkg-config
7732  */
7733 class TaskPkgConfig : public Task
7735 public:
7737     typedef enum
7738         {
7739         PKG_CONFIG_QUERY_CFLAGS,
7740         PKG_CONFIG_QUERY_LIBS,
7741         PKG_CONFIG_QUERY_ALL
7742         } QueryTypes;
7744     TaskPkgConfig(MakeBase &par) : Task(par)
7745         {
7746         type = TASK_PKG_CONFIG;
7747         name = "pkg-config";
7748         }
7750     virtual ~TaskPkgConfig()
7751         {}
7753     virtual bool execute()
7754         {
7755         String pkgName       = parent.eval(pkgNameOpt,      "");
7756         String prefix        = parent.eval(prefixOpt,       "");
7757         String propName      = parent.eval(propNameOpt,     "");
7758         String pkgConfigPath = parent.eval(pkgConfigPathOpt,"");
7759         String query         = parent.eval(queryOpt,        "all");
7761         String path = parent.resolve(pkgConfigPath);
7762         PkgConfig pkgconfig;
7763         pkgconfig.setPath(path);
7764         pkgconfig.setPrefix(prefix);
7765         if (!pkgconfig.query(pkgName))
7766             {
7767             error("<pkg-config> query failed for '%s", name.c_str());
7768             return false;
7769             }
7770             
7771         String val = "";
7772         if (query == "cflags")
7773             val = pkgconfig.getCflags();
7774         else if (query == "libs")
7775             val =pkgconfig.getLibs();
7776         else if (query == "all")
7777             val = pkgconfig.getAll();
7778         else
7779             {
7780             error("<pkg-config> unhandled query : %s", query.c_str());
7781             return false;
7782             }
7783         taskstatus("property %s = '%s'", propName.c_str(), val.c_str());
7784         parent.setProperty(propName, val);
7785         return true;
7786         }
7788     virtual bool parse(Element *elem)
7789         {
7790         //# NAME
7791         if (!parent.getAttribute(elem, "name", pkgNameOpt))
7792             return false;
7793         if (pkgNameOpt.size()==0)
7794             {
7795             error("<pkg-config> requires 'name=\"package\"' attribute");
7796             return false;
7797             }
7799         //# PROPERTY
7800         if (!parent.getAttribute(elem, "property", propNameOpt))
7801             return false;
7802         if (propNameOpt.size()==0)
7803             {
7804             error("<pkg-config> requires 'property=\"name\"' attribute");
7805             return false;
7806             }
7807         //# PATH
7808         if (!parent.getAttribute(elem, "path", pkgConfigPathOpt))
7809             return false;
7810         //# PREFIX
7811         if (!parent.getAttribute(elem, "prefix", prefixOpt))
7812             return false;
7813         //# QUERY
7814         if (!parent.getAttribute(elem, "query", queryOpt))
7815             return false;
7817         return true;
7818         }
7820 private:
7822     String queryOpt;
7823     String pkgNameOpt;
7824     String prefixOpt;
7825     String propNameOpt;
7826     String pkgConfigPathOpt;
7828 };
7835 /**
7836  *  Process an archive to allow random access
7837  */
7838 class TaskRanlib : public Task
7840 public:
7842     TaskRanlib(MakeBase &par) : Task(par)
7843         { type = TASK_RANLIB; name = "ranlib"; }
7845     virtual ~TaskRanlib()
7846         {}
7848     virtual bool execute()
7849         {
7850         String fileName = parent.eval(fileNameOpt, "");
7851         String command  = parent.eval(commandOpt, "ranlib");
7853         String fullName = parent.resolve(fileName);
7854         //trace("fullDir:%s", fullDir.c_str());
7855         String cmd = command;
7856         cmd.append(" ");
7857         cmd.append(fullName);
7858         String outbuf, errbuf;
7859         if (!executeCommand(cmd, "", outbuf, errbuf))
7860             return false;
7861         return true;
7862         }
7864     virtual bool parse(Element *elem)
7865         {
7866         if (!parent.getAttribute(elem, "command", commandOpt))
7867             return false;
7868         if (!parent.getAttribute(elem, "file", fileNameOpt))
7869             return false;
7870         if (fileNameOpt.size() == 0)
7871             {
7872             error("<ranlib> requires 'file=\"fileNname\"' attribute");
7873             return false;
7874             }
7875         return true;
7876         }
7878 private:
7880     String fileNameOpt;
7881     String commandOpt;
7882 };
7886 /**
7887  * Compile a resource file into a binary object
7888  */
7889 class TaskRC : public Task
7891 public:
7893     TaskRC(MakeBase &par) : Task(par)
7894         { type = TASK_RC; name = "rc"; }
7896     virtual ~TaskRC()
7897         {}
7899     virtual bool execute()
7900         {
7901         String command  = parent.eval(commandOpt,  "windres");
7902         String flags    = parent.eval(flagsOpt,    "");
7903         String fileName = parent.eval(fileNameOpt, "");
7904         String outName  = parent.eval(outNameOpt,  "");
7906         String fullFile = parent.resolve(fileName);
7907         String fullOut  = parent.resolve(outName);
7908         if (!isNewerThan(fullFile, fullOut))
7909             return true;
7910         String cmd = command;
7911         cmd.append(" -o ");
7912         cmd.append(fullOut);
7913         cmd.append(" ");
7914         cmd.append(flags);
7915         cmd.append(" ");
7916         cmd.append(fullFile);
7918         String outString, errString;
7919         if (!executeCommand(cmd.c_str(), "", outString, errString))
7920             {
7921             error("RC problem: %s", errString.c_str());
7922             return false;
7923             }
7924         return true;
7925         }
7927     virtual bool parse(Element *elem)
7928         {
7929         if (!parent.getAttribute(elem, "command", commandOpt))
7930             return false;
7931         if (!parent.getAttribute(elem, "file", fileNameOpt))
7932             return false;
7933         if (!parent.getAttribute(elem, "out", outNameOpt))
7934             return false;
7935         std::vector<Element *> children = elem->getChildren();
7936         for (unsigned int i=0 ; i<children.size() ; i++)
7937             {
7938             Element *child = children[i];
7939             String tagName = child->getName();
7940             if (tagName == "flags")
7941                 {
7942                 if (!parent.getValue(child, flagsOpt))
7943                     return false;
7944                 }
7945             }
7946         return true;
7947         }
7949 private:
7951     String commandOpt;
7952     String flagsOpt;
7953     String fileNameOpt;
7954     String outNameOpt;
7956 };
7960 /**
7961  *  Collect .o's into a .so or DLL
7962  */
7963 class TaskSharedLib : public Task
7965 public:
7967     TaskSharedLib(MakeBase &par) : Task(par)
7968         { type = TASK_SHAREDLIB; name = "dll"; }
7970     virtual ~TaskSharedLib()
7971         {}
7973     virtual bool execute()
7974         {
7975         String command     = parent.eval(commandOpt, "dllwrap");
7976         String fileName    = parent.eval(fileNameOpt, "");
7977         String defFileName = parent.eval(defFileNameOpt, "");
7978         String impFileName = parent.eval(impFileNameOpt, "");
7979         String libs        = parent.eval(libsOpt, "");
7981         //trace("###########HERE %d", fileSet.size());
7982         bool doit = false;
7983         
7984         String fullOut = parent.resolve(fileName);
7985         //trace("ar fullout: %s", fullOut.c_str());
7986         
7987         if (!listFiles(parent, fileSet))
7988             return false;
7989         String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
7991         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7992             {
7993             String fname;
7994             if (fileSetDir.size()>0)
7995                 {
7996                 fname.append(fileSetDir);
7997                 fname.append("/");
7998                 }
7999             fname.append(fileSet[i]);
8000             String fullName = parent.resolve(fname);
8001             //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
8002             if (isNewerThan(fullName, fullOut))
8003                 doit = true;
8004             }
8005         //trace("Needs it:%d", doit);
8006         if (!doit)
8007             {
8008             return true;
8009             }
8011         String cmd = "dllwrap";
8012         cmd.append(" -o ");
8013         cmd.append(fullOut);
8014         if (defFileName.size()>0)
8015             {
8016             cmd.append(" --def ");
8017             cmd.append(defFileName);
8018             cmd.append(" ");
8019             }
8020         if (impFileName.size()>0)
8021             {
8022             cmd.append(" --implib ");
8023             cmd.append(impFileName);
8024             cmd.append(" ");
8025             }
8026         for (unsigned int i=0 ; i<fileSet.size() ; i++)
8027             {
8028             String fname;
8029             if (fileSetDir.size()>0)
8030                 {
8031                 fname.append(fileSetDir);
8032                 fname.append("/");
8033                 }
8034             fname.append(fileSet[i]);
8035             String fullName = parent.resolve(fname);
8037             cmd.append(" ");
8038             cmd.append(fullName);
8039             }
8040         cmd.append(" ");
8041         cmd.append(libs);
8043         String outString, errString;
8044         if (!executeCommand(cmd.c_str(), "", outString, errString))
8045             {
8046             error("<sharedlib> problem: %s", errString.c_str());
8047             return false;
8048             }
8050         return true;
8051         }
8053     virtual bool parse(Element *elem)
8054         {
8055         if (!parent.getAttribute(elem, "command", commandOpt))
8056             return false;
8057         if (!parent.getAttribute(elem, "file", fileNameOpt))
8058             return false;
8059         if (!parent.getAttribute(elem, "import", impFileNameOpt))
8060             return false;
8061         if (!parent.getAttribute(elem, "def", defFileNameOpt))
8062             return false;
8063             
8064         std::vector<Element *> children = elem->getChildren();
8065         for (unsigned int i=0 ; i<children.size() ; i++)
8066             {
8067             Element *child = children[i];
8068             String tagName = child->getName();
8069             if (tagName == "fileset")
8070                 {
8071                 if (!parseFileSet(child, parent, fileSet))
8072                     return false;
8073                 }
8074             else if (tagName == "libs")
8075                 {
8076                 if (!parent.getValue(child, libsOpt))
8077                     return false;
8078                 libsOpt = strip(libsOpt);
8079                 }
8080             }
8081         return true;
8082         }
8084 private:
8086     FileSet fileSet;
8088     String commandOpt;
8089     String fileNameOpt;
8090     String defFileNameOpt;
8091     String impFileNameOpt;
8092     String libsOpt;
8094 };
8098 /**
8099  * Run the "ar" command to archive .o's into a .a
8100  */
8101 class TaskStaticLib : public Task
8103 public:
8105     TaskStaticLib(MakeBase &par) : Task(par)
8106         { type = TASK_STATICLIB; name = "staticlib"; }
8108     virtual ~TaskStaticLib()
8109         {}
8111     virtual bool execute()
8112         {
8113         String command = parent.eval(commandOpt, "ar crv");
8114         String fileName = parent.eval(fileNameOpt, "");
8116         bool doit = false;
8117         
8118         String fullOut = parent.resolve(fileName);
8119         //trace("ar fullout: %s", fullOut.c_str());
8120         
8121         if (!listFiles(parent, fileSet))
8122             return false;
8123         String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
8124         //trace("###########HERE %s", fileSetDir.c_str());
8126         for (unsigned int i=0 ; i<fileSet.size() ; i++)
8127             {
8128             String fname;
8129             if (fileSetDir.size()>0)
8130                 {
8131                 fname.append(fileSetDir);
8132                 fname.append("/");
8133                 }
8134             fname.append(fileSet[i]);
8135             String fullName = parent.resolve(fname);
8136             //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
8137             if (isNewerThan(fullName, fullOut))
8138                 doit = true;
8139             }
8140         //trace("Needs it:%d", doit);
8141         if (!doit)
8142             {
8143             return true;
8144             }
8146         String cmd = command;
8147         cmd.append(" ");
8148         cmd.append(fullOut);
8149         for (unsigned int i=0 ; i<fileSet.size() ; i++)
8150             {
8151             String fname;
8152             if (fileSetDir.size()>0)
8153                 {
8154                 fname.append(fileSetDir);
8155                 fname.append("/");
8156                 }
8157             fname.append(fileSet[i]);
8158             String fullName = parent.resolve(fname);
8160             cmd.append(" ");
8161             cmd.append(fullName);
8162             }
8164         String outString, errString;
8165         if (!executeCommand(cmd.c_str(), "", outString, errString))
8166             {
8167             error("<staticlib> problem: %s", errString.c_str());
8168             return false;
8169             }
8171         return true;
8172         }
8175     virtual bool parse(Element *elem)
8176         {
8177         if (!parent.getAttribute(elem, "command", commandOpt))
8178             return false;
8179         if (!parent.getAttribute(elem, "file", fileNameOpt))
8180             return false;
8181             
8182         std::vector<Element *> children = elem->getChildren();
8183         for (unsigned int i=0 ; i<children.size() ; i++)
8184             {
8185             Element *child = children[i];
8186             String tagName = child->getName();
8187             if (tagName == "fileset")
8188                 {
8189                 if (!parseFileSet(child, parent, fileSet))
8190                     return false;
8191                 }
8192             }
8193         return true;
8194         }
8196 private:
8198     FileSet fileSet;
8200     String commandOpt;
8201     String fileNameOpt;
8203 };
8208 /**
8209  * Strip an executable
8210  */
8211 class TaskStrip : public Task
8213 public:
8215     TaskStrip(MakeBase &par) : Task(par)
8216         { type = TASK_STRIP; name = "strip"; }
8218     virtual ~TaskStrip()
8219         {}
8221     virtual bool execute()
8222         {
8223         String command     = parent.eval(commandOpt, "strip");
8224         String fileName    = parent.eval(fileNameOpt, "");
8225         String symFileName = parent.eval(symFileNameOpt, "");
8227         String fullName = parent.resolve(fileName);
8228         //trace("fullDir:%s", fullDir.c_str());
8229         String cmd;
8230         String outbuf, errbuf;
8232         if (symFileName.size()>0)
8233             {
8234             String symFullName = parent.resolve(symFileName);
8235             cmd = "objcopy --only-keep-debug ";
8236             cmd.append(getNativePath(fullName));
8237             cmd.append(" ");
8238             cmd.append(getNativePath(symFullName));
8239             if (!executeCommand(cmd, "", outbuf, errbuf))
8240                 {
8241                 error("<strip> symbol file failed : %s", errbuf.c_str());
8242                 return false;
8243                 }
8244             }
8245             
8246         cmd = command;
8247         cmd.append(getNativePath(fullName));
8248         if (!executeCommand(cmd, "", outbuf, errbuf))
8249             {
8250             error("<strip> failed : %s", errbuf.c_str());
8251             return false;
8252             }
8253         return true;
8254         }
8256     virtual bool parse(Element *elem)
8257         {
8258         if (!parent.getAttribute(elem, "command", commandOpt))
8259             return false;
8260         if (!parent.getAttribute(elem, "file", fileNameOpt))
8261             return false;
8262         if (!parent.getAttribute(elem, "symfile", symFileNameOpt))
8263             return false;
8264         if (fileNameOpt.size() == 0)
8265             {
8266             error("<strip> requires 'file=\"fileName\"' attribute");
8267             return false;
8268             }
8269         return true;
8270         }
8272 private:
8274     String commandOpt;
8275     String fileNameOpt;
8276     String symFileNameOpt;
8277 };
8280 /**
8281  *
8282  */
8283 class TaskTouch : public Task
8285 public:
8287     TaskTouch(MakeBase &par) : Task(par)
8288         { type = TASK_TOUCH; name = "touch"; }
8290     virtual ~TaskTouch()
8291         {}
8293     virtual bool execute()
8294         {
8295         String fileName = parent.eval(fileNameOpt, "");
8297         String fullName = parent.resolve(fileName);
8298         String nativeFile = getNativePath(fullName);
8299         if (!isRegularFile(fullName) && !isDirectory(fullName))
8300             {            
8301             // S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH
8302             int ret = creat(nativeFile.c_str(), 0666);
8303             if (ret != 0) 
8304                 {
8305                 error("<touch> could not create '%s' : %s",
8306                     nativeFile.c_str(), strerror(ret));
8307                 return false;
8308                 }
8309             return true;
8310             }
8311         int ret = utime(nativeFile.c_str(), (struct utimbuf *)0);
8312         if (ret != 0)
8313             {
8314             error("<touch> could not update the modification time for '%s' : %s",
8315                 nativeFile.c_str(), strerror(ret));
8316             return false;
8317             }
8318         return true;
8319         }
8321     virtual bool parse(Element *elem)
8322         {
8323         //trace("touch parse");
8324         if (!parent.getAttribute(elem, "file", fileNameOpt))
8325             return false;
8326         if (fileNameOpt.size() == 0)
8327             {
8328             error("<touch> requires 'file=\"fileName\"' attribute");
8329             return false;
8330             }
8331         return true;
8332         }
8334     String fileNameOpt;
8335 };
8338 /**
8339  *
8340  */
8341 class TaskTstamp : public Task
8343 public:
8345     TaskTstamp(MakeBase &par) : Task(par)
8346         { type = TASK_TSTAMP; name = "tstamp"; }
8348     virtual ~TaskTstamp()
8349         {}
8351     virtual bool execute()
8352         {
8353         return true;
8354         }
8356     virtual bool parse(Element *elem)
8357         {
8358         //trace("tstamp parse");
8359         return true;
8360         }
8361 };
8365 /**
8366  *
8367  */
8368 Task *Task::createTask(Element *elem, int lineNr)
8370     String tagName = elem->getName();
8371     //trace("task:%s", tagName.c_str());
8372     Task *task = NULL;
8373     if (tagName == "cc")
8374         task = new TaskCC(parent);
8375     else if (tagName == "copy")
8376         task = new TaskCopy(parent);
8377     else if (tagName == "delete")
8378         task = new TaskDelete(parent);
8379     else if (tagName == "echo")
8380         task = new TaskEcho(parent);
8381     else if (tagName == "jar")
8382         task = new TaskJar(parent);
8383     else if (tagName == "javac")
8384         task = new TaskJavac(parent);
8385     else if (tagName == "link")
8386         task = new TaskLink(parent);
8387     else if (tagName == "makefile")
8388         task = new TaskMakeFile(parent);
8389     else if (tagName == "mkdir")
8390         task = new TaskMkDir(parent);
8391     else if (tagName == "msgfmt")
8392         task = new TaskMsgFmt(parent);
8393     else if (tagName == "pkg-config")
8394         task = new TaskPkgConfig(parent);
8395     else if (tagName == "ranlib")
8396         task = new TaskRanlib(parent);
8397     else if (tagName == "rc")
8398         task = new TaskRC(parent);
8399     else if (tagName == "sharedlib")
8400         task = new TaskSharedLib(parent);
8401     else if (tagName == "staticlib")
8402         task = new TaskStaticLib(parent);
8403     else if (tagName == "strip")
8404         task = new TaskStrip(parent);
8405     else if (tagName == "touch")
8406         task = new TaskTouch(parent);
8407     else if (tagName == "tstamp")
8408         task = new TaskTstamp(parent);
8409     else
8410         {
8411         error("Unknown task '%s'", tagName.c_str());
8412         return NULL;
8413         }
8415     task->setLine(lineNr);
8417     if (!task->parse(elem))
8418         {
8419         delete task;
8420         return NULL;
8421         }
8422     return task;
8427 //########################################################################
8428 //# T A R G E T
8429 //########################################################################
8431 /**
8432  *
8433  */
8434 class Target : public MakeBase
8437 public:
8439     /**
8440      *
8441      */
8442     Target(Make &par) : parent(par)
8443         { init(); }
8445     /**
8446      *
8447      */
8448     Target(const Target &other) : parent(other.parent)
8449         { init(); assign(other); }
8451     /**
8452      *
8453      */
8454     Target &operator=(const Target &other)
8455         { init(); assign(other); return *this; }
8457     /**
8458      *
8459      */
8460     virtual ~Target()
8461         { cleanup() ; }
8464     /**
8465      *
8466      */
8467     virtual Make &getParent()
8468         { return parent; }
8470     /**
8471      *
8472      */
8473     virtual String getName()
8474         { return name; }
8476     /**
8477      *
8478      */
8479     virtual void setName(const String &val)
8480         { name = val; }
8482     /**
8483      *
8484      */
8485     virtual String getDescription()
8486         { return description; }
8488     /**
8489      *
8490      */
8491     virtual void setDescription(const String &val)
8492         { description = val; }
8494     /**
8495      *
8496      */
8497     virtual void addDependency(const String &val)
8498         { deps.push_back(val); }
8500     /**
8501      *
8502      */
8503     virtual void parseDependencies(const String &val)
8504         { deps = tokenize(val, ", "); }
8506     /**
8507      *
8508      */
8509     virtual std::vector<String> &getDependencies()
8510         { return deps; }
8512     /**
8513      *
8514      */
8515     virtual String getIf()
8516         { return ifVar; }
8518     /**
8519      *
8520      */
8521     virtual void setIf(const String &val)
8522         { ifVar = val; }
8524     /**
8525      *
8526      */
8527     virtual String getUnless()
8528         { return unlessVar; }
8530     /**
8531      *
8532      */
8533     virtual void setUnless(const String &val)
8534         { unlessVar = val; }
8536     /**
8537      *
8538      */
8539     virtual void addTask(Task *val)
8540         { tasks.push_back(val); }
8542     /**
8543      *
8544      */
8545     virtual std::vector<Task *> &getTasks()
8546         { return tasks; }
8548 private:
8550     void init()
8551         {
8552         }
8554     void cleanup()
8555         {
8556         tasks.clear();
8557         }
8559     void assign(const Target &other)
8560         {
8561         //parent      = other.parent;
8562         name        = other.name;
8563         description = other.description;
8564         ifVar       = other.ifVar;
8565         unlessVar   = other.unlessVar;
8566         deps        = other.deps;
8567         tasks       = other.tasks;
8568         }
8570     Make &parent;
8572     String name;
8574     String description;
8576     String ifVar;
8578     String unlessVar;
8580     std::vector<String> deps;
8582     std::vector<Task *> tasks;
8584 };
8593 //########################################################################
8594 //# M A K E
8595 //########################################################################
8598 /**
8599  *
8600  */
8601 class Make : public MakeBase
8604 public:
8606     /**
8607      *
8608      */
8609     Make()
8610         { init(); }
8612     /**
8613      *
8614      */
8615     Make(const Make &other)
8616         { assign(other); }
8618     /**
8619      *
8620      */
8621     Make &operator=(const Make &other)
8622         { assign(other); return *this; }
8624     /**
8625      *
8626      */
8627     virtual ~Make()
8628         { cleanup(); }
8630     /**
8631      *
8632      */
8633     virtual std::map<String, Target> &getTargets()
8634         { return targets; }
8637     /**
8638      *
8639      */
8640     virtual String version()
8641         { return BUILDTOOL_VERSION; }
8643     /**
8644      * Overload a <property>
8645      */
8646     virtual bool specifyProperty(const String &name,
8647                                  const String &value);
8649     /**
8650      *
8651      */
8652     virtual bool run();
8654     /**
8655      *
8656      */
8657     virtual bool run(const String &target);
8661 private:
8663     /**
8664      *
8665      */
8666     void init();
8668     /**
8669      *
8670      */
8671     void cleanup();
8673     /**
8674      *
8675      */
8676     void assign(const Make &other);
8678     /**
8679      *
8680      */
8681     bool executeTask(Task &task);
8684     /**
8685      *
8686      */
8687     bool executeTarget(Target &target,
8688              std::set<String> &targetsCompleted);
8691     /**
8692      *
8693      */
8694     bool execute();
8696     /**
8697      *
8698      */
8699     bool checkTargetDependencies(Target &prop,
8700                     std::vector<String> &depList);
8702     /**
8703      *
8704      */
8705     bool parsePropertyFile(const String &fileName,
8706                            const String &prefix);
8708     /**
8709      *
8710      */
8711     bool parseProperty(Element *elem);
8713     /**
8714      *
8715      */
8716     bool parseFile();
8718     /**
8719      *
8720      */
8721     std::vector<String> glob(const String &pattern);
8724     //###############
8725     //# Fields
8726     //###############
8728     String projectName;
8730     String currentTarget;
8732     String defaultTarget;
8734     String specifiedTarget;
8736     String baseDir;
8738     String description;
8739     
8740     //std::vector<Property> properties;
8741     
8742     std::map<String, Target> targets;
8744     std::vector<Task *> allTasks;
8745     
8746     std::map<String, String> specifiedProperties;
8748 };
8751 //########################################################################
8752 //# C L A S S  M A I N T E N A N C E
8753 //########################################################################
8755 /**
8756  *
8757  */
8758 void Make::init()
8760     uri             = "build.xml";
8761     projectName     = "";
8762     currentTarget   = "";
8763     defaultTarget   = "";
8764     specifiedTarget = "";
8765     baseDir         = "";
8766     description     = "";
8767     envPrefix       = "env.";
8768     pcPrefix        = "pc.";
8769     pccPrefix       = "pcc.";
8770     pclPrefix       = "pcl.";
8771     properties.clear();
8772     for (unsigned int i = 0 ; i < allTasks.size() ; i++)
8773         delete allTasks[i];
8774     allTasks.clear();
8779 /**
8780  *
8781  */
8782 void Make::cleanup()
8784     for (unsigned int i = 0 ; i < allTasks.size() ; i++)
8785         delete allTasks[i];
8786     allTasks.clear();
8791 /**
8792  *
8793  */
8794 void Make::assign(const Make &other)
8796     uri              = other.uri;
8797     projectName      = other.projectName;
8798     currentTarget    = other.currentTarget;
8799     defaultTarget    = other.defaultTarget;
8800     specifiedTarget  = other.specifiedTarget;
8801     baseDir          = other.baseDir;
8802     description      = other.description;
8803     properties       = other.properties;
8808 //########################################################################
8809 //# U T I L I T Y    T A S K S
8810 //########################################################################
8812 /**
8813  *  Perform a file globbing
8814  */
8815 std::vector<String> Make::glob(const String &pattern)
8817     std::vector<String> res;
8818     return res;
8822 //########################################################################
8823 //# P U B L I C    A P I
8824 //########################################################################
8828 /**
8829  *
8830  */
8831 bool Make::executeTarget(Target &target,
8832              std::set<String> &targetsCompleted)
8835     String name = target.getName();
8837     //First get any dependencies for this target
8838     std::vector<String> deps = target.getDependencies();
8839     for (unsigned int i=0 ; i<deps.size() ; i++)
8840         {
8841         String dep = deps[i];
8842         //Did we do it already?  Skip
8843         if (targetsCompleted.find(dep)!=targetsCompleted.end())
8844             continue;
8845             
8846         std::map<String, Target> &tgts =
8847                target.getParent().getTargets();
8848         std::map<String, Target>::iterator iter =
8849                tgts.find(dep);
8850         if (iter == tgts.end())
8851             {
8852             error("Target '%s' dependency '%s' not found",
8853                       name.c_str(),  dep.c_str());
8854             return false;
8855             }
8856         Target depTarget = iter->second;
8857         if (!executeTarget(depTarget, targetsCompleted))
8858             {
8859             return false;
8860             }
8861         }
8863     status("##### Target : %s\n##### %s", name.c_str(),
8864             target.getDescription().c_str());
8866     //Now let's do the tasks
8867     std::vector<Task *> &tasks = target.getTasks();
8868     for (unsigned int i=0 ; i<tasks.size() ; i++)
8869         {
8870         Task *task = tasks[i];
8871         status("--- %s / %s", name.c_str(), task->getName().c_str());
8872         if (!task->execute())
8873             {
8874             return false;
8875             }
8876         }
8877         
8878     targetsCompleted.insert(name);
8879     
8880     return true;
8885 /**
8886  *  Main execute() method.  Start here and work
8887  *  up the dependency tree 
8888  */
8889 bool Make::execute()
8891     status("######## EXECUTE");
8893     //Determine initial target
8894     if (specifiedTarget.size()>0)
8895         {
8896         currentTarget = specifiedTarget;
8897         }
8898     else if (defaultTarget.size()>0)
8899         {
8900         currentTarget = defaultTarget;
8901         }
8902     else
8903         {
8904         error("execute: no specified or default target requested");
8905         return false;
8906         }
8908     std::map<String, Target>::iterator iter =
8909                targets.find(currentTarget);
8910     if (iter == targets.end())
8911         {
8912         error("Initial target '%s' not found",
8913                  currentTarget.c_str());
8914         return false;
8915         }
8916         
8917     //Now run
8918     Target target = iter->second;
8919     std::set<String> targetsCompleted;
8920     if (!executeTarget(target, targetsCompleted))
8921         {
8922         return false;
8923         }
8925     status("######## EXECUTE COMPLETE");
8926     return true;
8932 /**
8933  *
8934  */
8935 bool Make::checkTargetDependencies(Target &target, 
8936                             std::vector<String> &depList)
8938     String tgtName = target.getName().c_str();
8939     depList.push_back(tgtName);
8941     std::vector<String> deps = target.getDependencies();
8942     for (unsigned int i=0 ; i<deps.size() ; i++)
8943         {
8944         String dep = deps[i];
8945         //First thing entered was the starting Target
8946         if (dep == depList[0])
8947             {
8948             error("Circular dependency '%s' found at '%s'",
8949                       dep.c_str(), tgtName.c_str());
8950             std::vector<String>::iterator diter;
8951             for (diter=depList.begin() ; diter!=depList.end() ; diter++)
8952                 {
8953                 error("  %s", diter->c_str());
8954                 }
8955             return false;
8956             }
8958         std::map<String, Target> &tgts =
8959                   target.getParent().getTargets();
8960         std::map<String, Target>::iterator titer = tgts.find(dep);
8961         if (titer == tgts.end())
8962             {
8963             error("Target '%s' dependency '%s' not found",
8964                       tgtName.c_str(), dep.c_str());
8965             return false;
8966             }
8967         if (!checkTargetDependencies(titer->second, depList))
8968             {
8969             return false;
8970             }
8971         }
8972     return true;
8979 static int getword(int pos, const String &inbuf, String &result)
8981     int p = pos;
8982     int len = (int)inbuf.size();
8983     String val;
8984     while (p < len)
8985         {
8986         char ch = inbuf[p];
8987         if (!isalnum(ch) && ch!='.' && ch!='_')
8988             break;
8989         val.push_back(ch);
8990         p++;
8991         }
8992     result = val;
8993     return p;
8999 /**
9000  *
9001  */
9002 bool Make::parsePropertyFile(const String &fileName,
9003                              const String &prefix)
9005     FILE *f = fopen(fileName.c_str(), "r");
9006     if (!f)
9007         {
9008         error("could not open property file %s", fileName.c_str());
9009         return false;
9010         }
9011     int linenr = 0;
9012     while (!feof(f))
9013         {
9014         char buf[256];
9015         if (!fgets(buf, 255, f))
9016             break;
9017         linenr++;
9018         String s = buf;
9019         s = trim(s);
9020         int len = s.size();
9021         if (len == 0)
9022             continue;
9023         if (s[0] == '#')
9024             continue;
9025         String key;
9026         String val;
9027         int p = 0;
9028         int p2 = getword(p, s, key);
9029         if (p2 <= p)
9030             {
9031             error("property file %s, line %d: expected keyword",
9032                     fileName.c_str(), linenr);
9033             return false;
9034             }
9035         if (prefix.size() > 0)
9036             {
9037             key.insert(0, prefix);
9038             }
9040         //skip whitespace
9041         for (p=p2 ; p<len ; p++)
9042             if (!isspace(s[p]))
9043                 break;
9045         if (p>=len || s[p]!='=')
9046             {
9047             error("property file %s, line %d: expected '='",
9048                     fileName.c_str(), linenr);
9049             return false;
9050             }
9051         p++;
9053         //skip whitespace
9054         for ( ; p<len ; p++)
9055             if (!isspace(s[p]))
9056                 break;
9058         /* This way expects a word after the =
9059         p2 = getword(p, s, val);
9060         if (p2 <= p)
9061             {
9062             error("property file %s, line %d: expected value",
9063                     fileName.c_str(), linenr);
9064             return false;
9065             }
9066         */
9067         // This way gets the rest of the line after the =
9068         if (p>=len)
9069             {
9070             error("property file %s, line %d: expected value",
9071                     fileName.c_str(), linenr);
9072             return false;
9073             }
9074         val = s.substr(p);
9075         if (key.size()==0)
9076             continue;
9077         //allow property to be set, even if val=""
9079         //trace("key:'%s' val:'%s'", key.c_str(), val.c_str());
9080         //See if we wanted to overload this property
9081         std::map<String, String>::iterator iter =
9082             specifiedProperties.find(key);
9083         if (iter!=specifiedProperties.end())
9084             {
9085             val = iter->second;
9086             status("overloading property '%s' = '%s'",
9087                    key.c_str(), val.c_str());
9088             }
9089         properties[key] = val;
9090         }
9091     fclose(f);
9092     return true;
9098 /**
9099  *
9100  */
9101 bool Make::parseProperty(Element *elem)
9103     std::vector<Attribute> &attrs = elem->getAttributes();
9104     for (unsigned int i=0 ; i<attrs.size() ; i++)
9105         {
9106         String attrName = attrs[i].getName();
9107         String attrVal  = attrs[i].getValue();
9109         if (attrName == "name")
9110             {
9111             String val;
9112             if (!getAttribute(elem, "value", val))
9113                 return false;
9114             if (val.size() > 0)
9115                 {
9116                 properties[attrVal] = val;
9117                 }
9118             else
9119                 {
9120                 if (!getAttribute(elem, "location", val))
9121                     return false;
9122                 //let the property exist, even if not defined
9123                 properties[attrVal] = val;
9124                 }
9125             //See if we wanted to overload this property
9126             std::map<String, String>::iterator iter =
9127                 specifiedProperties.find(attrVal);
9128             if (iter != specifiedProperties.end())
9129                 {
9130                 val = iter->second;
9131                 status("overloading property '%s' = '%s'",
9132                     attrVal.c_str(), val.c_str());
9133                 properties[attrVal] = val;
9134                 }
9135             }
9136         else if (attrName == "file")
9137             {
9138             String prefix;
9139             if (!getAttribute(elem, "prefix", prefix))
9140                 return false;
9141             if (prefix.size() > 0)
9142                 {
9143                 if (prefix[prefix.size()-1] != '.')
9144                     prefix.push_back('.');
9145                 }
9146             if (!parsePropertyFile(attrName, prefix))
9147                 return false;
9148             }
9149         else if (attrName == "environment")
9150             {
9151             if (attrVal.find('.') != attrVal.npos)
9152                 {
9153                 error("environment prefix cannot have a '.' in it");
9154                 return false;
9155                 }
9156             envPrefix = attrVal;
9157             envPrefix.push_back('.');
9158             }
9159         else if (attrName == "pkg-config")
9160             {
9161             if (attrVal.find('.') != attrVal.npos)
9162                 {
9163                 error("pkg-config prefix cannot have a '.' in it");
9164                 return false;
9165                 }
9166             pcPrefix = attrVal;
9167             pcPrefix.push_back('.');
9168             }
9169         else if (attrName == "pkg-config-cflags")
9170             {
9171             if (attrVal.find('.') != attrVal.npos)
9172                 {
9173                 error("pkg-config-cflags prefix cannot have a '.' in it");
9174                 return false;
9175                 }
9176             pccPrefix = attrVal;
9177             pccPrefix.push_back('.');
9178             }
9179         else if (attrName == "pkg-config-libs")
9180             {
9181             if (attrVal.find('.') != attrVal.npos)
9182                 {
9183                 error("pkg-config-libs prefix cannot have a '.' in it");
9184                 return false;
9185                 }
9186             pclPrefix = attrVal;
9187             pclPrefix.push_back('.');
9188             }
9189         }
9191     return true;
9197 /**
9198  *
9199  */
9200 bool Make::parseFile()
9202     status("######## PARSE : %s", uri.getPath().c_str());
9204     setLine(0);
9206     Parser parser;
9207     Element *root = parser.parseFile(uri.getNativePath());
9208     if (!root)
9209         {
9210         error("Could not open %s for reading",
9211               uri.getNativePath().c_str());
9212         return false;
9213         }
9214     
9215     setLine(root->getLine());
9217     if (root->getChildren().size()==0 ||
9218         root->getChildren()[0]->getName()!="project")
9219         {
9220         error("Main xml element should be <project>");
9221         delete root;
9222         return false;
9223         }
9225     //########## Project attributes
9226     Element *project = root->getChildren()[0];
9227     String s = project->getAttribute("name");
9228     if (s.size() > 0)
9229         projectName = s;
9230     s = project->getAttribute("default");
9231     if (s.size() > 0)
9232         defaultTarget = s;
9233     s = project->getAttribute("basedir");
9234     if (s.size() > 0)
9235         baseDir = s;
9237     //######### PARSE MEMBERS
9238     std::vector<Element *> children = project->getChildren();
9239     for (unsigned int i=0 ; i<children.size() ; i++)
9240         {
9241         Element *elem = children[i];
9242         setLine(elem->getLine());
9243         String tagName = elem->getName();
9245         //########## DESCRIPTION
9246         if (tagName == "description")
9247             {
9248             description = parser.trim(elem->getValue());
9249             }
9251         //######### PROPERTY
9252         else if (tagName == "property")
9253             {
9254             if (!parseProperty(elem))
9255                 return false;
9256             }
9258         //######### TARGET
9259         else if (tagName == "target")
9260             {
9261             String tname   = elem->getAttribute("name");
9262             String tdesc   = elem->getAttribute("description");
9263             String tdeps   = elem->getAttribute("depends");
9264             String tif     = elem->getAttribute("if");
9265             String tunless = elem->getAttribute("unless");
9266             Target target(*this);
9267             target.setName(tname);
9268             target.setDescription(tdesc);
9269             target.parseDependencies(tdeps);
9270             target.setIf(tif);
9271             target.setUnless(tunless);
9272             std::vector<Element *> telems = elem->getChildren();
9273             for (unsigned int i=0 ; i<telems.size() ; i++)
9274                 {
9275                 Element *telem = telems[i];
9276                 Task breeder(*this);
9277                 Task *task = breeder.createTask(telem, telem->getLine());
9278                 if (!task)
9279                     return false;
9280                 allTasks.push_back(task);
9281                 target.addTask(task);
9282                 }
9284             //Check name
9285             if (tname.size() == 0)
9286                 {
9287                 error("no name for target");
9288                 return false;
9289                 }
9290             //Check for duplicate name
9291             if (targets.find(tname) != targets.end())
9292                 {
9293                 error("target '%s' already defined", tname.c_str());
9294                 return false;
9295                 }
9296             //more work than targets[tname]=target, but avoids default allocator
9297             targets.insert(std::make_pair<String, Target>(tname, target));
9298             }
9299         //######### none of the above
9300         else
9301             {
9302             error("unknown toplevel tag: <%s>", tagName.c_str());
9303             return false;
9304             }
9306         }
9308     std::map<String, Target>::iterator iter;
9309     for (iter = targets.begin() ; iter!= targets.end() ; iter++)
9310         {
9311         Target tgt = iter->second;
9312         std::vector<String> depList;
9313         if (!checkTargetDependencies(tgt, depList))
9314             {
9315             return false;
9316             }
9317         }
9320     delete root;
9321     status("######## PARSE COMPLETE");
9322     return true;
9326 /**
9327  * Overload a <property>
9328  */
9329 bool Make::specifyProperty(const String &name, const String &value)
9331     if (specifiedProperties.find(name) != specifiedProperties.end())
9332         {
9333         error("Property %s already specified", name.c_str());
9334         return false;
9335         }
9336     specifiedProperties[name] = value;
9337     return true;
9342 /**
9343  *
9344  */
9345 bool Make::run()
9347     if (!parseFile())
9348         return false;
9349         
9350     if (!execute())
9351         return false;
9353     return true;
9359 /**
9360  * Get a formatted MM:SS.sss time elapsed string
9361  */ 
9362 static String
9363 timeDiffString(struct timeval &x, struct timeval &y)
9365     long microsX  = x.tv_usec;
9366     long secondsX = x.tv_sec;
9367     long microsY  = y.tv_usec;
9368     long secondsY = y.tv_sec;
9369     if (microsX < microsY)
9370         {
9371         microsX += 1000000;
9372         secondsX -= 1;
9373         }
9375     int seconds = (int)(secondsX - secondsY);
9376     int millis  = (int)((microsX - microsY)/1000);
9378     int minutes = seconds/60;
9379     seconds -= minutes*60;
9380     char buf[80];
9381     snprintf(buf, 79, "%dm %d.%03ds", minutes, seconds, millis);
9382     String ret = buf;
9383     return ret;
9384     
9387 /**
9388  *
9389  */
9390 bool Make::run(const String &target)
9392     status("####################################################");
9393     status("#   %s", version().c_str());
9394     status("####################################################");
9395     struct timeval timeStart, timeEnd;
9396     ::gettimeofday(&timeStart, NULL);
9397     specifiedTarget = target;
9398     if (!run())
9399         return false;
9400     ::gettimeofday(&timeEnd, NULL);
9401     String timeStr = timeDiffString(timeEnd, timeStart);
9402     status("####################################################");
9403     status("#   BuildTool Completed : %s", timeStr.c_str());
9404     status("####################################################");
9405     return true;
9414 }// namespace buildtool
9415 //########################################################################
9416 //# M A I N
9417 //########################################################################
9419 typedef buildtool::String String;
9421 /**
9422  *  Format an error message in printf() style
9423  */
9424 static void error(const char *fmt, ...)
9426     va_list ap;
9427     va_start(ap, fmt);
9428     fprintf(stderr, "BuildTool error: ");
9429     vfprintf(stderr, fmt, ap);
9430     fprintf(stderr, "\n");
9431     va_end(ap);
9435 static bool parseProperty(const String &s, String &name, String &val)
9437     int len = s.size();
9438     int i;
9439     for (i=0 ; i<len ; i++)
9440         {
9441         char ch = s[i];
9442         if (ch == '=')
9443             break;
9444         name.push_back(ch);
9445         }
9446     if (i>=len || s[i]!='=')
9447         {
9448         error("property requires -Dname=value");
9449         return false;
9450         }
9451     i++;
9452     for ( ; i<len ; i++)
9453         {
9454         char ch = s[i];
9455         val.push_back(ch);
9456         }
9457     return true;
9461 /**
9462  * Compare a buffer with a key, for the length of the key
9463  */
9464 static bool sequ(const String &buf, const char *key)
9466     int len = buf.size();
9467     for (int i=0 ; key[i] && i<len ; i++)
9468         {
9469         if (key[i] != buf[i])
9470             return false;
9471         }        
9472     return true;
9475 static void usage(int argc, char **argv)
9477     printf("usage:\n");
9478     printf("   %s [options] [target]\n", argv[0]);
9479     printf("Options:\n");
9480     printf("  -help, -h              print this message\n");
9481     printf("  -version               print the version information and exit\n");
9482     printf("  -file <file>           use given buildfile\n");
9483     printf("  -f <file>                 ''\n");
9484     printf("  -D<property>=<value>   use value for given property\n");
9490 /**
9491  * Parse the command-line args, get our options,
9492  * and run this thing
9493  */   
9494 static bool parseOptions(int argc, char **argv)
9496     if (argc < 1)
9497         {
9498         error("Cannot parse arguments");
9499         return false;
9500         }
9502     buildtool::Make make;
9504     String target;
9506     //char *progName = argv[0];
9507     for (int i=1 ; i<argc ; i++)
9508         {
9509         String arg = argv[i];
9510         if (arg.size()>1 && arg[0]=='-')
9511             {
9512             if (arg == "-h" || arg == "-help")
9513                 {
9514                 usage(argc,argv);
9515                 return true;
9516                 }
9517             else if (arg == "-version")
9518                 {
9519                 printf("%s", make.version().c_str());
9520                 return true;
9521                 }
9522             else if (arg == "-f" || arg == "-file")
9523                 {
9524                 if (i>=argc)
9525                    {
9526                    usage(argc, argv);
9527                    return false;
9528                    }
9529                 i++; //eat option
9530                 make.setURI(argv[i]);
9531                 }
9532             else if (arg.size()>2 && sequ(arg, "-D"))
9533                 {
9534                 String s = arg.substr(2, arg.size());
9535                 String name, value;
9536                 if (!parseProperty(s, name, value))
9537                    {
9538                    usage(argc, argv);
9539                    return false;
9540                    }
9541                 if (!make.specifyProperty(name, value))
9542                     return false;
9543                 }
9544             else
9545                 {
9546                 error("Unknown option:%s", arg.c_str());
9547                 return false;
9548                 }
9549             }
9550         else
9551             {
9552             if (target.size()>0)
9553                 {
9554                 error("only one initial target");
9555                 usage(argc, argv);
9556                 return false;
9557                 }
9558             target = arg;
9559             }
9560         }
9562     //We have the options.  Now execute them
9563     if (!make.run(target))
9564         return false;
9566     return true;
9572 /*
9573 static bool runMake()
9575     buildtool::Make make;
9576     if (!make.run())
9577         return false;
9578     return true;
9582 static bool pkgConfigTest()
9584     buildtool::PkgConfig pkgConfig;
9585     if (!pkgConfig.readFile("gtk+-2.0.pc"))
9586         return false;
9587     return true;
9592 static bool depTest()
9594     buildtool::DepTool deptool;
9595     deptool.setSourceDirectory("/dev/ink/inkscape/src");
9596     if (!deptool.generateDependencies("build.dep"))
9597         return false;
9598     std::vector<buildtool::FileRec> res =
9599            deptool.loadDepFile("build.dep");
9600     if (res.size() == 0)
9601         return false;
9602     return true;
9605 static bool popenTest()
9607     buildtool::Make make;
9608     buildtool::String out, err;
9609     bool ret = make.executeCommand("gcc xx.cpp", "", out, err);
9610     printf("Popen test:%d '%s' '%s'\n", ret, out.c_str(), err.c_str());
9611     return true;
9615 static bool propFileTest()
9617     buildtool::Make make;
9618     make.parsePropertyFile("test.prop", "test.");
9619     return true;
9621 */
9623 int main(int argc, char **argv)
9626     if (!parseOptions(argc, argv))
9627         return 1;
9628     /*
9629     if (!popenTest())
9630         return 1;
9632     if (!depTest())
9633         return 1;
9634     if (!propFileTest())
9635         return 1;
9636     if (runMake())
9637         return 1;
9638     */
9639     return 0;
9643 //########################################################################
9644 //# E N D 
9645 //########################################################################