Code

rollup of buildtool/unix work. compiles and links inkscape
[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
3870 #ifdef __WIN32__
3872 /**
3873  * Execute a system call, using pipes to send data to the
3874  * program's stdin,  and reading stdout and stderr.
3875  */
3876 bool MakeBase::executeCommand(const String &command,
3877                               const String &inbuf,
3878                               String &outbuf,
3879                               String &errbuf)
3882     status("============ cmd ============\n%s\n=============================",
3883                 command.c_str());
3885     outbuf.clear();
3886     errbuf.clear();
3887     
3889     /*
3890     I really hate having win32 code in this program, but the
3891     read buffer in command.com and cmd.exe are just too small
3892     for the large commands we need for compiling and linking.
3893     */
3895     bool ret = true;
3897     //# Allocate a separate buffer for safety
3898     char *paramBuf = new char[command.size() + 1];
3899     if (!paramBuf)
3900        {
3901        error("executeCommand cannot allocate command buffer");
3902        return false;
3903        }
3904     strcpy(paramBuf, (char *)command.c_str());
3906     //# Go to http://msdn2.microsoft.com/en-us/library/ms682499.aspx
3907     //# to see how Win32 pipes work
3909     //# Create pipes
3910     SECURITY_ATTRIBUTES saAttr; 
3911     saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); 
3912     saAttr.bInheritHandle = TRUE; 
3913     saAttr.lpSecurityDescriptor = NULL; 
3914     HANDLE stdinRead,  stdinWrite;
3915     HANDLE stdoutRead, stdoutWrite;
3916     HANDLE stderrRead, stderrWrite;
3917     if (!CreatePipe(&stdinRead, &stdinWrite, &saAttr, 0))
3918         {
3919         error("executeProgram: could not create pipe");
3920         delete[] paramBuf;
3921         return false;
3922         } 
3923     SetHandleInformation(stdinWrite, HANDLE_FLAG_INHERIT, 0);
3924     if (!CreatePipe(&stdoutRead, &stdoutWrite, &saAttr, 0))
3925         {
3926         error("executeProgram: could not create pipe");
3927         delete[] paramBuf;
3928         return false;
3929         } 
3930     SetHandleInformation(stdoutRead, HANDLE_FLAG_INHERIT, 0);
3931     if (!CreatePipe(&stderrRead, &stderrWrite, &saAttr, 0))
3932         {
3933         error("executeProgram: could not create pipe");
3934         delete[] paramBuf;
3935         return false;
3936         } 
3937     SetHandleInformation(stderrRead, HANDLE_FLAG_INHERIT, 0);
3939     // Create the process
3940     STARTUPINFO siStartupInfo;
3941     PROCESS_INFORMATION piProcessInfo;
3942     memset(&siStartupInfo, 0, sizeof(siStartupInfo));
3943     memset(&piProcessInfo, 0, sizeof(piProcessInfo));
3944     siStartupInfo.cb = sizeof(siStartupInfo);
3945     siStartupInfo.hStdError   =  stderrWrite;
3946     siStartupInfo.hStdOutput  =  stdoutWrite;
3947     siStartupInfo.hStdInput   =  stdinRead;
3948     siStartupInfo.dwFlags    |=  STARTF_USESTDHANDLES;
3949    
3950     if (!CreateProcess(NULL, paramBuf, NULL, NULL, true,
3951                 0, NULL, NULL, &siStartupInfo,
3952                 &piProcessInfo))
3953         {
3954         error("executeCommand : could not create process : %s",
3955                     win32LastError().c_str());
3956         ret = false;
3957         }
3959     delete[] paramBuf;
3961     DWORD bytesWritten;
3962     if (inbuf.size()>0 &&
3963         !WriteFile(stdinWrite, inbuf.c_str(), inbuf.size(), 
3964                &bytesWritten, NULL))
3965         {
3966         error("executeCommand: could not write to pipe");
3967         return false;
3968         }    
3969     if (!CloseHandle(stdinWrite))
3970         {          
3971         error("executeCommand: could not close write pipe");
3972         return false;
3973         }
3974     if (!CloseHandle(stdoutWrite))
3975         {
3976         error("executeCommand: could not close read pipe");
3977         return false;
3978         }
3979     if (!CloseHandle(stderrWrite))
3980         {
3981         error("executeCommand: could not close read pipe");
3982         return false;
3983         }
3985     bool lastLoop = false;
3986     while (true)
3987         {
3988         DWORD avail;
3989         DWORD bytesRead;
3990         char readBuf[4096];
3992         //trace("## stderr");
3993         PeekNamedPipe(stderrRead, NULL, 0, NULL, &avail, NULL);
3994         if (avail > 0)
3995             {
3996             bytesRead = 0;
3997             if (avail>4096) avail = 4096;
3998             ReadFile(stderrRead, readBuf, avail, &bytesRead, NULL);
3999             if (bytesRead > 0)
4000                 {
4001                 for (unsigned int i=0 ; i<bytesRead ; i++)
4002                     errbuf.push_back(readBuf[i]);
4003                 }
4004             }
4006         //trace("## stdout");
4007         PeekNamedPipe(stdoutRead, NULL, 0, NULL, &avail, NULL);
4008         if (avail > 0)
4009             {
4010             bytesRead = 0;
4011             if (avail>4096) avail = 4096;
4012             ReadFile(stdoutRead, readBuf, avail, &bytesRead, NULL);
4013             if (bytesRead > 0)
4014                 {
4015                 for (unsigned int i=0 ; i<bytesRead ; i++)
4016                     outbuf.push_back(readBuf[i]);
4017                 }
4018             }
4019             
4020         //Was this the final check after program done?
4021         if (lastLoop)
4022             break;
4024         DWORD exitCode;
4025         GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
4026         if (exitCode != STILL_ACTIVE)
4027             lastLoop = true;
4029         Sleep(10);
4030         }    
4031     //trace("outbuf:%s", outbuf.c_str());
4032     if (!CloseHandle(stdoutRead))
4033         {
4034         error("executeCommand: could not close read pipe");
4035         return false;
4036         }
4037     if (!CloseHandle(stderrRead))
4038         {
4039         error("executeCommand: could not close read pipe");
4040         return false;
4041         }
4043     DWORD exitCode;
4044     GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
4045     //trace("exit code:%d", exitCode);
4046     if (exitCode != 0)
4047         {
4048         ret = false;
4049         }
4050     
4051     CloseHandle(piProcessInfo.hProcess);
4052     CloseHandle(piProcessInfo.hThread);
4054     return ret;
4056
4058 #else  /*do it unix style*/
4060 #include <sys/wait.h>
4064 /**
4065  * Execute a system call, using pipes to send data to the
4066  * program's stdin,  and reading stdout and stderr.
4067  */
4068 bool MakeBase::executeCommand(const String &command,
4069                               const String &inbuf,
4070                               String &outbuf,
4071                               String &errbuf)
4074     status("============ cmd ============\n%s\n=============================",
4075                 command.c_str());
4077     outbuf.clear();
4078     errbuf.clear();
4079     
4081     int outfds[2];
4082     if (pipe(outfds) < 0)
4083         return false;
4084     int errfds[2];
4085     if (pipe(errfds) < 0)
4086         return false;
4087     int pid = fork();
4088     if (pid < 0)
4089         {
4090         close(outfds[0]);
4091         close(outfds[1]);
4092         close(errfds[0]);
4093         close(errfds[1]);
4094         error("launch of command '%s' failed : %s",
4095              command.c_str(), strerror(errno));
4096         return false;
4097         }
4098     else if (pid > 0) // parent
4099         {
4100         close(outfds[1]);
4101         close(errfds[1]);
4102         }
4103     else // == 0, child
4104         {
4105         close(outfds[0]);
4106         dup2(outfds[1], STDOUT_FILENO);
4107         close(outfds[1]);
4108         close(errfds[0]);
4109         dup2(errfds[1], STDERR_FILENO);
4110         close(errfds[1]);
4112         char *args[4];
4113         args[0] = (char *)"sh";
4114         args[1] = (char *)"-c";
4115         args[2] = (char *)command.c_str();
4116         args[3] = NULL;
4117         execv("/bin/sh", args);
4118         _exit(EXIT_FAILURE);
4119         }
4121     String outb;
4122     String errb;
4124     int outRead = outfds[0];
4125     int errRead = errfds[0];
4126     int max = outRead;
4127     if (errRead > max)
4128         max = errRead;
4130     bool outOpen = true;
4131     bool errOpen = true;
4133     while (outOpen || errOpen)
4134         {
4135         char ch;
4136         fd_set fdset;
4137         FD_ZERO(&fdset);
4138         if (outOpen)
4139             FD_SET(outRead, &fdset);
4140         if (errOpen)
4141             FD_SET(errRead, &fdset);
4142         int ret = select(max+1, &fdset, NULL, NULL, NULL);
4143         if (ret < 0)
4144             break;
4145         if (FD_ISSET(outRead, &fdset))
4146             {
4147             if (read(outRead, &ch, 1) <= 0)
4148                 outOpen = false;
4149             else if (ch <= 0)
4150                 outOpen = false;
4151             else
4152                 outb.push_back(ch);
4153             }
4154         if (FD_ISSET(errRead, &fdset))
4155             {
4156             if (read(errRead, &ch, 1) <= 0)
4157                 errOpen = false;
4158             else if (ch <= 0)
4159                 errOpen = false;
4160             else
4161                 errb.push_back(ch);
4162             }
4163         }
4165     int childReturnValue;
4166     wait(&childReturnValue);
4168     close(outRead);
4169     close(errRead);
4171     outbuf = outb;
4172     errbuf = errb;
4174     if (childReturnValue != 0)
4175         {
4176         error("exec of command '%s' failed : %s",
4177              command.c_str(), strerror(childReturnValue));
4178         return false;
4179         }
4181     return true;
4182
4184 #endif
4189 bool MakeBase::listDirectories(const String &baseName,
4190                               const String &dirName,
4191                               std::vector<String> &res)
4193     res.push_back(dirName);
4194     String fullPath = baseName;
4195     if (dirName.size()>0)
4196         {
4197         fullPath.append("/");
4198         fullPath.append(dirName);
4199         }
4200     DIR *dir = opendir(fullPath.c_str());
4201     while (true)
4202         {
4203         struct dirent *de = readdir(dir);
4204         if (!de)
4205             break;
4207         //Get the directory member name
4208         String s = de->d_name;
4209         if (s.size() == 0 || s[0] == '.')
4210             continue;
4211         String childName = dirName;
4212         childName.append("/");
4213         childName.append(s);
4215         String fullChildPath = baseName;
4216         fullChildPath.append("/");
4217         fullChildPath.append(childName);
4218         struct stat finfo;
4219         String childNative = getNativePath(fullChildPath);
4220         if (stat(childNative.c_str(), &finfo)<0)
4221             {
4222             error("cannot stat file:%s", childNative.c_str());
4223             }
4224         else if (S_ISDIR(finfo.st_mode))
4225             {
4226             //trace("directory: %s", childName.c_str());
4227             if (!listDirectories(baseName, childName, res))
4228                 return false;
4229             }
4230         }
4231     closedir(dir);
4233     return true;
4237 bool MakeBase::listFiles(const String &baseDir,
4238                          const String &dirName,
4239                          std::vector<String> &res)
4241     String fullDir = baseDir;
4242     if (dirName.size()>0)
4243         {
4244         fullDir.append("/");
4245         fullDir.append(dirName);
4246         }
4247     String dirNative = getNativePath(fullDir);
4249     std::vector<String> subdirs;
4250     DIR *dir = opendir(dirNative.c_str());
4251     if (!dir)
4252         {
4253         error("Could not open directory %s : %s",
4254               dirNative.c_str(), strerror(errno));
4255         return false;
4256         }
4257     while (true)
4258         {
4259         struct dirent *de = readdir(dir);
4260         if (!de)
4261             break;
4263         //Get the directory member name
4264         String s = de->d_name;
4265         if (s.size() == 0 || s[0] == '.')
4266             continue;
4267         String childName;
4268         if (dirName.size()>0)
4269             {
4270             childName.append(dirName);
4271             childName.append("/");
4272             }
4273         childName.append(s);
4274         String fullChild = baseDir;
4275         fullChild.append("/");
4276         fullChild.append(childName);
4277         
4278         if (isDirectory(fullChild))
4279             {
4280             //trace("directory: %s", childName.c_str());
4281             if (!listFiles(baseDir, childName, res))
4282                 return false;
4283             continue;
4284             }
4285         else if (!isRegularFile(fullChild))
4286             {
4287             error("unknown file:%s", childName.c_str());
4288             return false;
4289             }
4291        //all done!
4292         res.push_back(childName);
4294         }
4295     closedir(dir);
4297     return true;
4301 /**
4302  * Several different classes extend MakeBase.  By "propRef", we mean
4303  * the one holding the properties.  Likely "Make" itself
4304  */
4305 bool MakeBase::listFiles(MakeBase &propRef, FileSet &fileSet)
4307     //before doing the list,  resolve any property references
4308     //that might have been specified in the directory name, such as ${src}
4309     String fsDir = fileSet.getDirectory();
4310     String dir;
4311     if (!propRef.getSubstitutions(fsDir, dir))
4312         return false;
4313     String baseDir = propRef.resolve(dir);
4314     std::vector<String> fileList;
4315     if (!listFiles(baseDir, "", fileList))
4316         return false;
4318     std::vector<String> includes = fileSet.getIncludes();
4319     std::vector<String> excludes = fileSet.getExcludes();
4321     std::vector<String> incs;
4322     std::vector<String>::iterator iter;
4324     std::sort(fileList.begin(), fileList.end());
4326     //If there are <includes>, then add files to the output
4327     //in the order of the include list
4328     if (includes.size()==0)
4329         incs = fileList;
4330     else
4331         {
4332         for (iter = includes.begin() ; iter != includes.end() ; iter++)
4333             {
4334             String &pattern = *iter;
4335             std::vector<String>::iterator siter;
4336             for (siter = fileList.begin() ; siter != fileList.end() ; siter++)
4337                 {
4338                 String s = *siter;
4339                 if (regexMatch(s, pattern))
4340                     {
4341                     //trace("INCLUDED:%s", s.c_str());
4342                     incs.push_back(s);
4343                     }
4344                 }
4345             }
4346         }
4348     //Now trim off the <excludes>
4349     std::vector<String> res;
4350     for (iter = incs.begin() ; iter != incs.end() ; iter++)
4351         {
4352         String s = *iter;
4353         bool skipme = false;
4354         std::vector<String>::iterator siter;
4355         for (siter = excludes.begin() ; siter != excludes.end() ; siter++)
4356             {
4357             String &pattern = *siter;
4358             if (regexMatch(s, pattern))
4359                 {
4360                 //trace("EXCLUDED:%s", s.c_str());
4361                 skipme = true;
4362                 break;
4363                 }
4364             }
4365         if (!skipme)
4366             res.push_back(s);
4367         }
4368         
4369     fileSet.setFiles(res);
4371     return true;
4375 /**
4376  * 0 == all, 1 = cflags, 2 = libs
4377  */ 
4378 bool MakeBase::pkgConfigRecursive(const String packageName,
4379                                   const String &path, 
4380                                   const String &prefix, 
4381                                   int query,
4382                                   String &result,
4383                                   std::set<String> &deplist) 
4385     PkgConfig pkgConfig;
4386     if (path.size() > 0)
4387         pkgConfig.setPath(path);
4388     if (prefix.size() > 0)
4389         pkgConfig.setPrefix(prefix);
4390     if (!pkgConfig.query(packageName))
4391         return false;
4392     if (query == 0)
4393         result = pkgConfig.getAll();
4394     else if (query == 1)
4395         result = pkgConfig.getCflags();
4396     else
4397         result = pkgConfig.getLibs();
4398     deplist.insert(packageName);
4399     std::vector<String> list = pkgConfig.getRequireList();
4400     for (unsigned int i = 0 ; i<list.size() ; i++)
4401         {
4402         String depPkgName = list[i];
4403         if (deplist.find(depPkgName) != deplist.end())
4404             continue;
4405         String val;
4406         if (!pkgConfigRecursive(depPkgName, path, prefix, query, val, deplist))
4407             {
4408             error("Based on 'requires' attribute of package '%s'", packageName.c_str());
4409             return false;
4410             }
4411         result.append(" ");
4412         result.append(val);
4413         }
4415     return true;
4418 bool MakeBase::pkgConfigQuery(const String &packageName, int query, String &result)
4420     std::set<String> deplist;
4421     String path = getProperty("pkg-config-path");
4422     if (path.size()>0)
4423         path = resolve(path);
4424     String prefix = getProperty("pkg-config-prefix");
4425     String val;
4426     if (!pkgConfigRecursive(packageName, path, prefix, query, val, deplist))
4427         return false;
4428     result = val;
4429     return true;
4434 /**
4435  * replace a variable ref like ${a} with a value
4436  */
4437 bool MakeBase::lookupProperty(const String &propertyName, String &result)
4439     String varname = propertyName;
4440     if (envPrefix.size() > 0 &&
4441         varname.compare(0, envPrefix.size(), envPrefix) == 0)
4442         {
4443         varname = varname.substr(envPrefix.size());
4444         char *envstr = getenv(varname.c_str());
4445         if (!envstr)
4446             {
4447             error("environment variable '%s' not defined", varname.c_str());
4448             return false;
4449             }
4450         result = envstr;
4451         }
4452     else if (pcPrefix.size() > 0 &&
4453         varname.compare(0, pcPrefix.size(), pcPrefix) == 0)
4454         {
4455         varname = varname.substr(pcPrefix.size());
4456         String val;
4457         if (!pkgConfigQuery(varname, 0, val))
4458             return false;
4459         result = val;
4460         }
4461     else if (pccPrefix.size() > 0 &&
4462         varname.compare(0, pccPrefix.size(), pccPrefix) == 0)
4463         {
4464         varname = varname.substr(pccPrefix.size());
4465         String val;
4466         if (!pkgConfigQuery(varname, 1, val))
4467             return false;
4468         result = val;
4469         }
4470     else if (pclPrefix.size() > 0 &&
4471         varname.compare(0, pclPrefix.size(), pclPrefix) == 0)
4472         {
4473         varname = varname.substr(pclPrefix.size());
4474         String val;
4475         if (!pkgConfigQuery(varname, 2, val))
4476             return false;
4477         result = val;
4478         }
4479     else
4480         {
4481         std::map<String, String>::iterator iter;
4482         iter = properties.find(varname);
4483         if (iter != properties.end())
4484             {
4485             result = iter->second;
4486             }
4487         else
4488             {
4489             error("property '%s' not found", varname.c_str());
4490             return false;
4491             }
4492         }
4493     return true;
4499 /**
4500  * Analyse a string, looking for any substitutions or other
4501  * things that need resolution 
4502  */
4503 bool MakeBase::getSubstitutionsRecursive(const String &str,
4504                                          String &result, int depth)
4506     if (depth > 10)
4507         {
4508         error("nesting of substitutions too deep (>10) for '%s'",
4509                         str.c_str());
4510         return false;
4511         }
4512     String s = trim(str);
4513     int len = (int)s.size();
4514     String val;
4515     for (int i=0 ; i<len ; i++)
4516         {
4517         char ch = s[i];
4518         if (ch == '$' && s[i+1] == '{')
4519             {
4520             String varname;
4521             int j = i+2;
4522             for ( ; j<len ; j++)
4523                 {
4524                 ch = s[j];
4525                 if (ch == '$' && s[j+1] == '{')
4526                     {
4527                     error("attribute %s cannot have nested variable references",
4528                            s.c_str());
4529                     return false;
4530                     }
4531                 else if (ch == '}')
4532                     {
4533                     varname = trim(varname);
4534                     String varval;
4535                     if (!lookupProperty(varname, varval))
4536                         return false;
4537                     String varval2;
4538                     //Now see if the answer has ${} in it, too
4539                     if (!getSubstitutionsRecursive(varval, varval2, depth + 1))
4540                         return false;
4541                     val.append(varval2);
4542                     break;
4543                     }
4544                 else
4545                     {
4546                     varname.push_back(ch);
4547                     }
4548                 }
4549             i = j;
4550             }
4551         else
4552             {
4553             val.push_back(ch);
4554             }
4555         }
4556     result = val;
4557     return true;
4560 /**
4561  * Analyse a string, looking for any substitutions or other
4562  * things that need resilution 
4563  */
4564 bool MakeBase::getSubstitutions(const String &str, String &result)
4566     return getSubstitutionsRecursive(str, result, 0);
4571 /**
4572  * replace variable refs like ${a} with their values
4573  * Assume that the string has already been syntax validated
4574  */
4575 String MakeBase::eval(const String &s, const String &defaultVal)
4577     if (s.size()==0)
4578         return defaultVal;
4579     String ret;
4580     if (getSubstitutions(s, ret))
4581         return ret;
4582     else
4583         return defaultVal;
4587 /**
4588  * replace variable refs like ${a} with their values
4589  * return true or false
4590  * Assume that the string has already been syntax validated
4591  */
4592 bool MakeBase::evalBool(const String &s, bool defaultVal)
4594     if (s.size()==0)
4595         return defaultVal;
4596     String val = eval(s, "false");
4597     if (val.size()==0)
4598         return defaultVal;
4599     if (val == "true" || val == "TRUE")
4600         return true;
4601     else
4602         return false;
4606 /**
4607  * Get a string attribute, testing it for proper syntax and
4608  * property names.
4609  */
4610 bool MakeBase::getAttribute(Element *elem, const String &name,
4611                                     String &result)
4613     String s = elem->getAttribute(name);
4614     String tmp;
4615     bool ret = getSubstitutions(s, tmp);
4616     if (ret)
4617         result = s;  //assign -if- ok
4618     return ret;
4622 /**
4623  * Get a string value, testing it for proper syntax and
4624  * property names.
4625  */
4626 bool MakeBase::getValue(Element *elem, String &result)
4628     String s = elem->getValue();
4629     String tmp;
4630     bool ret = getSubstitutions(s, tmp);
4631     if (ret)
4632         result = s;  //assign -if- ok
4633     return ret;
4639 /**
4640  * Parse a <patternset> entry
4641  */  
4642 bool MakeBase::parsePatternSet(Element *elem,
4643                           MakeBase &propRef,
4644                           std::vector<String> &includes,
4645                           std::vector<String> &excludes
4646                           )
4648     std::vector<Element *> children  = elem->getChildren();
4649     for (unsigned int i=0 ; i<children.size() ; i++)
4650         {
4651         Element *child = children[i];
4652         String tagName = child->getName();
4653         if (tagName == "exclude")
4654             {
4655             String fname;
4656             if (!propRef.getAttribute(child, "name", fname))
4657                 return false;
4658             //trace("EXCLUDE: %s", fname.c_str());
4659             excludes.push_back(fname);
4660             }
4661         else if (tagName == "include")
4662             {
4663             String fname;
4664             if (!propRef.getAttribute(child, "name", fname))
4665                 return false;
4666             //trace("INCLUDE: %s", fname.c_str());
4667             includes.push_back(fname);
4668             }
4669         }
4671     return true;
4677 /**
4678  * Parse a <fileset> entry, and determine which files
4679  * should be included
4680  */  
4681 bool MakeBase::parseFileSet(Element *elem,
4682                           MakeBase &propRef,
4683                           FileSet &fileSet)
4685     String name = elem->getName();
4686     if (name != "fileset")
4687         {
4688         error("expected <fileset>");
4689         return false;
4690         }
4693     std::vector<String> includes;
4694     std::vector<String> excludes;
4696     //A fileset has one implied patternset
4697     if (!parsePatternSet(elem, propRef, includes, excludes))
4698         {
4699         return false;
4700         }
4701     //Look for child tags, including more patternsets
4702     std::vector<Element *> children  = elem->getChildren();
4703     for (unsigned int i=0 ; i<children.size() ; i++)
4704         {
4705         Element *child = children[i];
4706         String tagName = child->getName();
4707         if (tagName == "patternset")
4708             {
4709             if (!parsePatternSet(child, propRef, includes, excludes))
4710                 {
4711                 return false;
4712                 }
4713             }
4714         }
4716     String dir;
4717     //Now do the stuff
4718     //Get the base directory for reading file names
4719     if (!propRef.getAttribute(elem, "dir", dir))
4720         return false;
4722     fileSet.setDirectory(dir);
4723     fileSet.setIncludes(includes);
4724     fileSet.setExcludes(excludes);
4725     
4726     /*
4727     std::vector<String> fileList;
4728     if (dir.size() > 0)
4729         {
4730         String baseDir = propRef.resolve(dir);
4731         if (!listFiles(baseDir, "", includes, excludes, fileList))
4732             return false;
4733         }
4734     std::sort(fileList.begin(), fileList.end());
4735     result = fileList;
4736     */
4738     
4739     /*
4740     for (unsigned int i=0 ; i<result.size() ; i++)
4741         {
4742         trace("RES:%s", result[i].c_str());
4743         }
4744     */
4746     
4747     return true;
4750 /**
4751  * Parse a <filelist> entry.  This is far simpler than FileSet,
4752  * since no directory scanning is needed.  The file names are listed
4753  * explicitly.
4754  */  
4755 bool MakeBase::parseFileList(Element *elem,
4756                           MakeBase &propRef,
4757                           FileList &fileList)
4759     std::vector<String> fnames;
4760     //Look for child tags, namely "file"
4761     std::vector<Element *> children  = elem->getChildren();
4762     for (unsigned int i=0 ; i<children.size() ; i++)
4763         {
4764         Element *child = children[i];
4765         String tagName = child->getName();
4766         if (tagName == "file")
4767             {
4768             String fname = child->getAttribute("name");
4769             if (fname.size()==0)
4770                 {
4771                 error("<file> element requires name="" attribute");
4772                 return false;
4773                 }
4774             fnames.push_back(fname);
4775             }
4776         else
4777             {
4778             error("tag <%s> not allowed in <fileset>", tagName.c_str());
4779             return false;
4780             }
4781         }
4783     String dir;
4784     //Get the base directory for reading file names
4785     if (!propRef.getAttribute(elem, "dir", dir))
4786         return false;
4787     fileList.setDirectory(dir);
4788     fileList.setFiles(fnames);
4790     return true;
4795 /**
4796  * Create a directory, making intermediate dirs
4797  * if necessary
4798  */                  
4799 bool MakeBase::createDirectory(const String &dirname)
4801     //trace("## createDirectory: %s", dirname.c_str());
4802     //## first check if it exists
4803     struct stat finfo;
4804     String nativeDir = getNativePath(dirname);
4805     char *cnative = (char *) nativeDir.c_str();
4806 #ifdef __WIN32__
4807     if (strlen(cnative)==2 && cnative[1]==':')
4808         return true;
4809 #endif
4810     if (stat(cnative, &finfo)==0)
4811         {
4812         if (!S_ISDIR(finfo.st_mode))
4813             {
4814             error("mkdir: file %s exists but is not a directory",
4815                   cnative);
4816             return false;
4817             }
4818         else //exists
4819             {
4820             return true;
4821             }
4822         }
4824     //## 2: pull off the last path segment, if any,
4825     //## to make the dir 'above' this one, if necessary
4826     unsigned int pos = dirname.find_last_of('/');
4827     if (pos>0 && pos != dirname.npos)
4828         {
4829         String subpath = dirname.substr(0, pos);
4830         //A letter root (c:) ?
4831         if (!createDirectory(subpath))
4832             return false;
4833         }
4834         
4835     //## 3: now make
4836 #ifdef __WIN32__
4837     if (mkdir(cnative)<0)
4838 #else
4839     if (mkdir(cnative, S_IRWXU | S_IRWXG | S_IRWXO)<0)
4840 #endif
4841         {
4842         error("cannot make directory '%s' : %s",
4843                  cnative, strerror(errno));
4844         return false;
4845         }
4846         
4847     return true;
4851 /**
4852  * Remove a directory recursively
4853  */ 
4854 bool MakeBase::removeDirectory(const String &dirName)
4856     char *dname = (char *)dirName.c_str();
4858     DIR *dir = opendir(dname);
4859     if (!dir)
4860         {
4861         //# Let this fail nicely.
4862         return true;
4863         //error("error opening directory %s : %s", dname, strerror(errno));
4864         //return false;
4865         }
4866     
4867     while (true)
4868         {
4869         struct dirent *de = readdir(dir);
4870         if (!de)
4871             break;
4873         //Get the directory member name
4874         String s = de->d_name;
4875         if (s.size() == 0 || s[0] == '.')
4876             continue;
4877         String childName;
4878         if (dirName.size() > 0)
4879             {
4880             childName.append(dirName);
4881             childName.append("/");
4882             }
4883         childName.append(s);
4886         struct stat finfo;
4887         String childNative = getNativePath(childName);
4888         char *cnative = (char *)childNative.c_str();
4889         if (stat(cnative, &finfo)<0)
4890             {
4891             error("cannot stat file:%s", cnative);
4892             }
4893         else if (S_ISDIR(finfo.st_mode))
4894             {
4895             //trace("DEL dir: %s", childName.c_str());
4896             if (!removeDirectory(childName))
4897                 {
4898                 return false;
4899                 }
4900             }
4901         else if (!S_ISREG(finfo.st_mode))
4902             {
4903             //trace("not regular: %s", cnative);
4904             }
4905         else
4906             {
4907             //trace("DEL file: %s", childName.c_str());
4908             if (remove(cnative)<0)
4909                 {
4910                 error("error deleting %s : %s",
4911                      cnative, strerror(errno));
4912                 return false;
4913                 }
4914             }
4915         }
4916     closedir(dir);
4918     //Now delete the directory
4919     String native = getNativePath(dirName);
4920     if (rmdir(native.c_str())<0)
4921         {
4922         error("could not delete directory %s : %s",
4923             native.c_str() , strerror(errno));
4924         return false;
4925         }
4927     return true;
4928     
4932 /**
4933  * Copy a file from one name to another. Perform only if needed
4934  */ 
4935 bool MakeBase::copyFile(const String &srcFile, const String &destFile)
4937     //# 1 Check up-to-date times
4938     String srcNative = getNativePath(srcFile);
4939     struct stat srcinfo;
4940     if (stat(srcNative.c_str(), &srcinfo)<0)
4941         {
4942         error("source file %s for copy does not exist",
4943                  srcNative.c_str());
4944         return false;
4945         }
4947     String destNative = getNativePath(destFile);
4948     struct stat destinfo;
4949     if (stat(destNative.c_str(), &destinfo)==0)
4950         {
4951         if (destinfo.st_mtime >= srcinfo.st_mtime)
4952             return true;
4953         }
4954         
4955     //# 2 prepare a destination directory if necessary
4956     unsigned int pos = destFile.find_last_of('/');
4957     if (pos != destFile.npos)
4958         {
4959         String subpath = destFile.substr(0, pos);
4960         if (!createDirectory(subpath))
4961             return false;
4962         }
4964     //# 3 do the data copy
4965 #ifndef __WIN32__
4967     FILE *srcf = fopen(srcNative.c_str(), "rb");
4968     if (!srcf)
4969         {
4970         error("copyFile cannot open '%s' for reading", srcNative.c_str());
4971         return false;
4972         }
4973     FILE *destf = fopen(destNative.c_str(), "wb");
4974     if (!destf)
4975         {
4976         error("copyFile cannot open %s for writing", srcNative.c_str());
4977         return false;
4978         }
4980     while (!feof(srcf))
4981         {
4982         int ch = fgetc(srcf);
4983         if (ch<0)
4984             break;
4985         fputc(ch, destf);
4986         }
4988     fclose(destf);
4989     fclose(srcf);
4991 #else
4992     
4993     if (!CopyFile(srcNative.c_str(), destNative.c_str(), false))
4994         {
4995         error("copyFile from %s to %s failed",
4996              srcNative.c_str(), destNative.c_str());
4997         return false;
4998         }
4999         
5000 #endif /* __WIN32__ */
5003     return true;
5008 /**
5009  * Tests if the file exists and is a regular file
5010  */ 
5011 bool MakeBase::isRegularFile(const String &fileName)
5013     String native = getNativePath(fileName);
5014     struct stat finfo;
5015     
5016     //Exists?
5017     if (stat(native.c_str(), &finfo)<0)
5018         return false;
5021     //check the file mode
5022     if (!S_ISREG(finfo.st_mode))
5023         return false;
5025     return true;
5028 /**
5029  * Tests if the file exists and is a directory
5030  */ 
5031 bool MakeBase::isDirectory(const String &fileName)
5033     String native = getNativePath(fileName);
5034     struct stat finfo;
5035     
5036     //Exists?
5037     if (stat(native.c_str(), &finfo)<0)
5038         return false;
5041     //check the file mode
5042     if (!S_ISDIR(finfo.st_mode))
5043         return false;
5045     return true;
5050 /**
5051  * Tests is the modification of fileA is newer than fileB
5052  */ 
5053 bool MakeBase::isNewerThan(const String &fileA, const String &fileB)
5055     //trace("isNewerThan:'%s' , '%s'", fileA.c_str(), fileB.c_str());
5056     String nativeA = getNativePath(fileA);
5057     struct stat infoA;
5058     //IF source does not exist, NOT newer
5059     if (stat(nativeA.c_str(), &infoA)<0)
5060         {
5061         return false;
5062         }
5064     String nativeB = getNativePath(fileB);
5065     struct stat infoB;
5066     //IF dest does not exist, YES, newer
5067     if (stat(nativeB.c_str(), &infoB)<0)
5068         {
5069         return true;
5070         }
5072     //check the actual times
5073     if (infoA.st_mtime > infoB.st_mtime)
5074         {
5075         return true;
5076         }
5078     return false;
5082 //########################################################################
5083 //# P K G    C O N F I G
5084 //########################################################################
5087 /**
5088  * Get a character from the buffer at pos.  If out of range,
5089  * return -1 for safety
5090  */
5091 int PkgConfig::get(int pos)
5093     if (pos>parselen)
5094         return -1;
5095     return parsebuf[pos];
5100 /**
5101  *  Skip over all whitespace characters beginning at pos.  Return
5102  *  the position of the first non-whitespace character.
5103  *  Pkg-config is line-oriented, so check for newline
5104  */
5105 int PkgConfig::skipwhite(int pos)
5107     while (pos < parselen)
5108         {
5109         int ch = get(pos);
5110         if (ch < 0)
5111             break;
5112         if (!isspace(ch))
5113             break;
5114         pos++;
5115         }
5116     return pos;
5120 /**
5121  *  Parse the buffer beginning at pos, for a word.  Fill
5122  *  'ret' with the result.  Return the position after the
5123  *  word.
5124  */
5125 int PkgConfig::getword(int pos, String &ret)
5127     while (pos < parselen)
5128         {
5129         int ch = get(pos);
5130         if (ch < 0)
5131             break;
5132         if (!isalnum(ch) && ch != '_' && ch != '-' && ch != '+' && ch != '.')
5133             break;
5134         ret.push_back((char)ch);
5135         pos++;
5136         }
5137     return pos;
5140 bool PkgConfig::parseRequires()
5142     if (requires.size() == 0)
5143         return true;
5144     parsebuf = (char *)requires.c_str();
5145     parselen = requires.size();
5146     int pos = 0;
5147     while (pos < parselen)
5148         {
5149         pos = skipwhite(pos);
5150         String val;
5151         int pos2 = getword(pos, val);
5152         if (pos2 == pos)
5153             break;
5154         pos = pos2;
5155         //trace("val %s", val.c_str());
5156         requireList.push_back(val);
5157         }
5158     return true;
5162 static int getint(const String str)
5164     char *s = (char *)str.c_str();
5165     char *ends = NULL;
5166     long val = strtol(s, &ends, 10);
5167     if (ends == s)
5168         return 0L;
5169     else
5170         return val;
5173 void PkgConfig::parseVersion()
5175     if (version.size() == 0)
5176         return;
5177     String s1, s2, s3;
5178     unsigned int pos = 0;
5179     unsigned int pos2 = version.find('.', pos);
5180     if (pos2 == version.npos)
5181         {
5182         s1 = version;
5183         }
5184     else
5185         {
5186         s1 = version.substr(pos, pos2-pos);
5187         pos = pos2;
5188         pos++;
5189         if (pos < version.size())
5190             {
5191             pos2 = version.find('.', pos);
5192             if (pos2 == version.npos)
5193                 {
5194                 s2 = version.substr(pos, version.size()-pos);
5195                 }
5196             else
5197                 {
5198                 s2 = version.substr(pos, pos2-pos);
5199                 pos = pos2;
5200                 pos++;
5201                 if (pos < version.size())
5202                     s3 = version.substr(pos, pos2-pos);
5203                 }
5204             }
5205         }
5207     majorVersion = getint(s1);
5208     minorVersion = getint(s2);
5209     microVersion = getint(s3);
5210     //trace("version:%d.%d.%d", majorVersion,
5211     //          minorVersion, microVersion );
5215 bool PkgConfig::parseLine(const String &lineBuf)
5217     parsebuf = (char *)lineBuf.c_str();
5218     parselen = lineBuf.size();
5219     int pos = 0;
5220     
5221     while (pos < parselen)
5222         {
5223         String attrName;
5224         pos = skipwhite(pos);
5225         int ch = get(pos);
5226         if (ch == '#')
5227             {
5228             //comment.  eat the rest of the line
5229             while (pos < parselen)
5230                 {
5231                 ch = get(pos);
5232                 if (ch == '\n' || ch < 0)
5233                     break;
5234                 pos++;
5235                 }
5236             continue;
5237             }
5238         pos = getword(pos, attrName);
5239         if (attrName.size() == 0)
5240             continue;
5241         
5242         pos = skipwhite(pos);
5243         ch = get(pos);
5244         if (ch != ':' && ch != '=')
5245             {
5246             error("expected ':' or '='");
5247             return false;
5248             }
5249         pos++;
5250         pos = skipwhite(pos);
5251         String attrVal;
5252         while (pos < parselen)
5253             {
5254             ch = get(pos);
5255             if (ch == '\n' || ch < 0)
5256                 break;
5257             else if (ch == '$' && get(pos+1) == '{')
5258                 {
5259                 //#  this is a ${substitution}
5260                 pos += 2;
5261                 String subName;
5262                 while (pos < parselen)
5263                     {
5264                     ch = get(pos);
5265                     if (ch < 0)
5266                         {
5267                         error("unterminated substitution");
5268                         return false;
5269                         }
5270                     else if (ch == '}')
5271                         break;
5272                     else
5273                         subName.push_back((char)ch);
5274                     pos++;
5275                     }
5276                 //trace("subName:%s %s", subName.c_str(), prefix.c_str());
5277                 if (subName == "prefix" && prefix.size()>0)
5278                     {
5279                     attrVal.append(prefix);
5280                     //trace("prefix override:%s", prefix.c_str());
5281                     }
5282                 else
5283                     {
5284                     String subVal = attrs[subName];
5285                     //trace("subVal:%s", subVal.c_str());
5286                     attrVal.append(subVal);
5287                     }
5288                 }
5289             else
5290                 attrVal.push_back((char)ch);
5291             pos++;
5292             }
5294         attrVal = trim(attrVal);
5295         attrs[attrName] = attrVal;
5297         String attrNameL = toLower(attrName);
5299         if (attrNameL == "name")
5300             name = attrVal;
5301         else if (attrNameL == "description")
5302             description = attrVal;
5303         else if (attrNameL == "cflags")
5304             cflags = attrVal;
5305         else if (attrNameL == "libs")
5306             libs = attrVal;
5307         else if (attrNameL == "requires")
5308             requires = attrVal;
5309         else if (attrNameL == "version")
5310             version = attrVal;
5312         //trace("name:'%s'  value:'%s'",
5313         //      attrName.c_str(), attrVal.c_str());
5314         }
5316     return true;
5320 bool PkgConfig::parse(const String &buf)
5322     init();
5324     String line;
5325     int lineNr = 0;
5326     for (unsigned int p=0 ; p<buf.size() ; p++)
5327         {
5328         int ch = buf[p];
5329         if (ch == '\n' || ch == '\r')
5330             {
5331             if (!parseLine(line))
5332                 return false;
5333             line.clear();
5334             lineNr++;
5335             }
5336         else
5337             {
5338             line.push_back(ch);
5339             }
5340         }
5341     if (line.size()>0)
5342         {
5343         if (!parseLine(line))
5344             return false;
5345         }
5347     parseRequires();
5348     parseVersion();
5350     return true;
5356 void PkgConfig::dumpAttrs()
5358     //trace("### PkgConfig attributes for %s", fileName.c_str());
5359     std::map<String, String>::iterator iter;
5360     for (iter=attrs.begin() ; iter!=attrs.end() ; iter++)
5361         {
5362         trace("   %s = %s", iter->first.c_str(), iter->second.c_str());
5363         }
5367 bool PkgConfig::readFile(const String &fname)
5369     fileName = getNativePath(fname);
5371     FILE *f = fopen(fileName.c_str(), "r");
5372     if (!f)
5373         {
5374         error("cannot open file '%s' for reading", fileName.c_str());
5375         return false;
5376         }
5377     String buf;
5378     while (true)
5379         {
5380         int ch = fgetc(f);
5381         if (ch < 0)
5382             break;
5383         buf.push_back((char)ch);
5384         }
5385     fclose(f);
5387     //trace("####### File:\n%s", buf.c_str());
5388     if (!parse(buf))
5389         {
5390         return false;
5391         }
5393     //dumpAttrs();
5395     return true;
5400 bool PkgConfig::query(const String &pkgName)
5402     name = pkgName;
5404     String fname = path;
5405     fname.append("/");
5406     fname.append(name);
5407     fname.append(".pc");
5409     if (!readFile(fname))
5410         return false;
5411     
5412     return true;
5419 //########################################################################
5420 //# D E P T O O L
5421 //########################################################################
5425 /**
5426  *  Class which holds information for each file.
5427  */
5428 class FileRec
5430 public:
5432     typedef enum
5433         {
5434         UNKNOWN,
5435         CFILE,
5436         HFILE,
5437         OFILE
5438         } FileType;
5440     /**
5441      *  Constructor
5442      */
5443     FileRec()
5444         { init(); type = UNKNOWN; }
5446     /**
5447      *  Copy constructor
5448      */
5449     FileRec(const FileRec &other)
5450         { init(); assign(other); }
5451     /**
5452      *  Constructor
5453      */
5454     FileRec(int typeVal)
5455         { init(); type = typeVal; }
5456     /**
5457      *  Assignment operator
5458      */
5459     FileRec &operator=(const FileRec &other)
5460         { init(); assign(other); return *this; }
5463     /**
5464      *  Destructor
5465      */
5466     ~FileRec()
5467         {}
5469     /**
5470      *  Directory part of the file name
5471      */
5472     String path;
5474     /**
5475      *  Base name, sans directory and suffix
5476      */
5477     String baseName;
5479     /**
5480      *  File extension, such as cpp or h
5481      */
5482     String suffix;
5484     /**
5485      *  Type of file: CFILE, HFILE, OFILE
5486      */
5487     int type;
5489     /**
5490      * Used to list files ref'd by this one
5491      */
5492     std::map<String, FileRec *> files;
5495 private:
5497     void init()
5498         {
5499         }
5501     void assign(const FileRec &other)
5502         {
5503         type     = other.type;
5504         baseName = other.baseName;
5505         suffix   = other.suffix;
5506         files    = other.files;
5507         }
5509 };
5513 /**
5514  *  Simpler dependency record
5515  */
5516 class DepRec
5518 public:
5520     /**
5521      *  Constructor
5522      */
5523     DepRec()
5524         {init();}
5526     /**
5527      *  Copy constructor
5528      */
5529     DepRec(const DepRec &other)
5530         {init(); assign(other);}
5531     /**
5532      *  Constructor
5533      */
5534     DepRec(const String &fname)
5535         {init(); name = fname; }
5536     /**
5537      *  Assignment operator
5538      */
5539     DepRec &operator=(const DepRec &other)
5540         {init(); assign(other); return *this;}
5543     /**
5544      *  Destructor
5545      */
5546     ~DepRec()
5547         {}
5549     /**
5550      *  Directory part of the file name
5551      */
5552     String path;
5554     /**
5555      *  Base name, without the path and suffix
5556      */
5557     String name;
5559     /**
5560      *  Suffix of the source
5561      */
5562     String suffix;
5565     /**
5566      * Used to list files ref'd by this one
5567      */
5568     std::vector<String> files;
5571 private:
5573     void init()
5574         {
5575         }
5577     void assign(const DepRec &other)
5578         {
5579         path     = other.path;
5580         name     = other.name;
5581         suffix   = other.suffix;
5582         files    = other.files; //avoid recursion
5583         }
5585 };
5588 class DepTool : public MakeBase
5590 public:
5592     /**
5593      *  Constructor
5594      */
5595     DepTool()
5596         { init(); }
5598     /**
5599      *  Copy constructor
5600      */
5601     DepTool(const DepTool &other)
5602         { init(); assign(other); }
5604     /**
5605      *  Assignment operator
5606      */
5607     DepTool &operator=(const DepTool &other)
5608         { init(); assign(other); return *this; }
5611     /**
5612      *  Destructor
5613      */
5614     ~DepTool()
5615         {}
5618     /**
5619      *  Reset this section of code
5620      */
5621     virtual void init();
5622     
5623     /**
5624      *  Reset this section of code
5625      */
5626     virtual void assign(const DepTool &other)
5627         {
5628         }
5629     
5630     /**
5631      *  Sets the source directory which will be scanned
5632      */
5633     virtual void setSourceDirectory(const String &val)
5634         { sourceDir = val; }
5636     /**
5637      *  Returns the source directory which will be scanned
5638      */
5639     virtual String getSourceDirectory()
5640         { return sourceDir; }
5642     /**
5643      *  Sets the list of files within the directory to analyze
5644      */
5645     virtual void setFileList(const std::vector<String> &list)
5646         { fileList = list; }
5648     /**
5649      * Creates the list of all file names which will be
5650      * candidates for further processing.  Reads make.exclude
5651      * to see which files for directories to leave out.
5652      */
5653     virtual bool createFileList();
5656     /**
5657      *  Generates the forward dependency list
5658      */
5659     virtual bool generateDependencies();
5662     /**
5663      *  Generates the forward dependency list, saving the file
5664      */
5665     virtual bool generateDependencies(const String &);
5668     /**
5669      *  Load a dependency file
5670      */
5671     std::vector<DepRec> loadDepFile(const String &fileName);
5673     /**
5674      *  Load a dependency file, generating one if necessary
5675      */
5676     std::vector<DepRec> getDepFile(const String &fileName,
5677               bool forceRefresh);
5679     /**
5680      *  Save a dependency file
5681      */
5682     bool saveDepFile(const String &fileName);
5685 private:
5688     /**
5689      *
5690      */
5691     void parseName(const String &fullname,
5692                    String &path,
5693                    String &basename,
5694                    String &suffix);
5696     /**
5697      *
5698      */
5699     int get(int pos);
5701     /**
5702      *
5703      */
5704     int skipwhite(int pos);
5706     /**
5707      *
5708      */
5709     int getword(int pos, String &ret);
5711     /**
5712      *
5713      */
5714     bool sequ(int pos, const char *key);
5716     /**
5717      *
5718      */
5719     bool addIncludeFile(FileRec *frec, const String &fname);
5721     /**
5722      *
5723      */
5724     bool scanFile(const String &fname, FileRec *frec);
5726     /**
5727      *
5728      */
5729     bool processDependency(FileRec *ofile, FileRec *include);
5731     /**
5732      *
5733      */
5734     String sourceDir;
5736     /**
5737      *
5738      */
5739     std::vector<String> fileList;
5741     /**
5742      *
5743      */
5744     std::vector<String> directories;
5746     /**
5747      * A list of all files which will be processed for
5748      * dependencies.
5749      */
5750     std::map<String, FileRec *> allFiles;
5752     /**
5753      * The list of .o files, and the
5754      * dependencies upon them.
5755      */
5756     std::map<String, FileRec *> oFiles;
5758     int depFileSize;
5759     char *depFileBuf;
5761     static const int readBufSize = 8192;
5762     char readBuf[8193];//byte larger
5764 };
5770 /**
5771  *  Clean up after processing.  Called by the destructor, but should
5772  *  also be called before the object is reused.
5773  */
5774 void DepTool::init()
5776     sourceDir = ".";
5778     fileList.clear();
5779     directories.clear();
5780     
5781     //clear output file list
5782     std::map<String, FileRec *>::iterator iter;
5783     for (iter=oFiles.begin(); iter!=oFiles.end() ; iter++)
5784         delete iter->second;
5785     oFiles.clear();
5787     //allFiles actually contains the master copies. delete them
5788     for (iter= allFiles.begin(); iter!=allFiles.end() ; iter++)
5789         delete iter->second;
5790     allFiles.clear(); 
5797 /**
5798  *  Parse a full path name into path, base name, and suffix
5799  */
5800 void DepTool::parseName(const String &fullname,
5801                         String &path,
5802                         String &basename,
5803                         String &suffix)
5805     if (fullname.size() < 2)
5806         return;
5808     unsigned int pos = fullname.find_last_of('/');
5809     if (pos != fullname.npos && pos<fullname.size()-1)
5810         {
5811         path = fullname.substr(0, pos);
5812         pos++;
5813         basename = fullname.substr(pos, fullname.size()-pos);
5814         }
5815     else
5816         {
5817         path = "";
5818         basename = fullname;
5819         }
5821     pos = basename.find_last_of('.');
5822     if (pos != basename.npos && pos<basename.size()-1)
5823         {
5824         suffix   = basename.substr(pos+1, basename.size()-pos-1);
5825         basename = basename.substr(0, pos);
5826         }
5828     //trace("parsename:%s %s %s", path.c_str(),
5829     //        basename.c_str(), suffix.c_str()); 
5834 /**
5835  *  Generate our internal file list.
5836  */
5837 bool DepTool::createFileList()
5840     for (unsigned int i=0 ; i<fileList.size() ; i++)
5841         {
5842         String fileName = fileList[i];
5843         //trace("## FileName:%s", fileName.c_str());
5844         String path;
5845         String basename;
5846         String sfx;
5847         parseName(fileName, path, basename, sfx);
5848         if (sfx == "cpp" || sfx == "c" || sfx == "cxx"   ||
5849             sfx == "cc" || sfx == "CC")
5850             {
5851             FileRec *fe         = new FileRec(FileRec::CFILE);
5852             fe->path            = path;
5853             fe->baseName        = basename;
5854             fe->suffix          = sfx;
5855             allFiles[fileName]  = fe;
5856             }
5857         else if (sfx == "h"   ||  sfx == "hh"  ||
5858                  sfx == "hpp" ||  sfx == "hxx")
5859             {
5860             FileRec *fe         = new FileRec(FileRec::HFILE);
5861             fe->path            = path;
5862             fe->baseName        = basename;
5863             fe->suffix          = sfx;
5864             allFiles[fileName]  = fe;
5865             }
5866         }
5868     if (!listDirectories(sourceDir, "", directories))
5869         return false;
5870         
5871     return true;
5878 /**
5879  * Get a character from the buffer at pos.  If out of range,
5880  * return -1 for safety
5881  */
5882 int DepTool::get(int pos)
5884     if (pos>depFileSize)
5885         return -1;
5886     return depFileBuf[pos];
5891 /**
5892  *  Skip over all whitespace characters beginning at pos.  Return
5893  *  the position of the first non-whitespace character.
5894  */
5895 int DepTool::skipwhite(int pos)
5897     while (pos < depFileSize)
5898         {
5899         int ch = get(pos);
5900         if (ch < 0)
5901             break;
5902         if (!isspace(ch))
5903             break;
5904         pos++;
5905         }
5906     return pos;
5910 /**
5911  *  Parse the buffer beginning at pos, for a word.  Fill
5912  *  'ret' with the result.  Return the position after the
5913  *  word.
5914  */
5915 int DepTool::getword(int pos, String &ret)
5917     while (pos < depFileSize)
5918         {
5919         int ch = get(pos);
5920         if (ch < 0)
5921             break;
5922         if (isspace(ch))
5923             break;
5924         ret.push_back((char)ch);
5925         pos++;
5926         }
5927     return pos;
5930 /**
5931  * Return whether the sequence of characters in the buffer
5932  * beginning at pos match the key,  for the length of the key
5933  */
5934 bool DepTool::sequ(int pos, const char *key)
5936     while (*key)
5937         {
5938         if (*key != get(pos))
5939             return false;
5940         key++; pos++;
5941         }
5942     return true;
5947 /**
5948  *  Add an include file name to a file record.  If the name
5949  *  is not found in allFiles explicitly, try prepending include
5950  *  directory names to it and try again.
5951  */
5952 bool DepTool::addIncludeFile(FileRec *frec, const String &iname)
5954     //# if the name is an exact match to a path name
5955     //# in allFiles, like "myinc.h"
5956     std::map<String, FileRec *>::iterator iter =
5957            allFiles.find(iname);
5958     if (iter != allFiles.end()) //already exists
5959         {
5960          //h file in same dir
5961         FileRec *other = iter->second;
5962         //trace("local: '%s'", iname.c_str());
5963         frec->files[iname] = other;
5964         return true;
5965         }
5966     else 
5967         {
5968         //## Ok, it was not found directly
5969         //look in other dirs
5970         std::vector<String>::iterator diter;
5971         for (diter=directories.begin() ;
5972              diter!=directories.end() ; diter++)
5973             {
5974             String dfname = *diter;
5975             dfname.append("/");
5976             dfname.append(iname);
5977             URI fullPathURI(dfname);  //normalize path name
5978             String fullPath = fullPathURI.getPath();
5979             if (fullPath[0] == '/')
5980                 fullPath = fullPath.substr(1);
5981             //trace("Normalized %s to %s", dfname.c_str(), fullPath.c_str());
5982             iter = allFiles.find(fullPath);
5983             if (iter != allFiles.end())
5984                 {
5985                 FileRec *other = iter->second;
5986                 //trace("other: '%s'", iname.c_str());
5987                 frec->files[fullPath] = other;
5988                 return true;
5989                 }
5990             }
5991         }
5992     return true;
5997 /**
5998  *  Lightly parse a file to find the #include directives.  Do
5999  *  a bit of state machine stuff to make sure that the directive
6000  *  is valid.  (Like not in a comment).
6001  */
6002 bool DepTool::scanFile(const String &fname, FileRec *frec)
6004     String fileName;
6005     if (sourceDir.size() > 0)
6006         {
6007         fileName.append(sourceDir);
6008         fileName.append("/");
6009         }
6010     fileName.append(fname);
6011     String nativeName = getNativePath(fileName);
6012     FILE *f = fopen(nativeName.c_str(), "r");
6013     if (!f)
6014         {
6015         error("Could not open '%s' for reading", fname.c_str());
6016         return false;
6017         }
6018     String buf;
6019     while (!feof(f))
6020         {
6021         int nrbytes = fread(readBuf, 1, readBufSize, f);
6022         readBuf[nrbytes] = '\0';
6023         buf.append(readBuf);
6024         }
6025     fclose(f);
6027     depFileSize = buf.size();
6028     depFileBuf  = (char *)buf.c_str();
6029     int pos = 0;
6032     while (pos < depFileSize)
6033         {
6034         //trace("p:%c", get(pos));
6036         //# Block comment
6037         if (get(pos) == '/' && get(pos+1) == '*')
6038             {
6039             pos += 2;
6040             while (pos < depFileSize)
6041                 {
6042                 if (get(pos) == '*' && get(pos+1) == '/')
6043                     {
6044                     pos += 2;
6045                     break;
6046                     }
6047                 else
6048                     pos++;
6049                 }
6050             }
6051         //# Line comment
6052         else if (get(pos) == '/' && get(pos+1) == '/')
6053             {
6054             pos += 2;
6055             while (pos < depFileSize)
6056                 {
6057                 if (get(pos) == '\n')
6058                     {
6059                     pos++;
6060                     break;
6061                     }
6062                 else
6063                     pos++;
6064                 }
6065             }
6066         //# #include! yaay
6067         else if (sequ(pos, "#include"))
6068             {
6069             pos += 8;
6070             pos = skipwhite(pos);
6071             String iname;
6072             pos = getword(pos, iname);
6073             if (iname.size()>2)
6074                 {
6075                 iname = iname.substr(1, iname.size()-2);
6076                 addIncludeFile(frec, iname);
6077                 }
6078             }
6079         else
6080             {
6081             pos++;
6082             }
6083         }
6085     return true;
6090 /**
6091  *  Recursively check include lists to find all files in allFiles to which
6092  *  a given file is dependent.
6093  */
6094 bool DepTool::processDependency(FileRec *ofile, FileRec *include)
6096     std::map<String, FileRec *>::iterator iter;
6097     for (iter=include->files.begin() ; iter!=include->files.end() ; iter++)
6098         {
6099         String fname  = iter->first;
6100         if (ofile->files.find(fname) != ofile->files.end())
6101             {
6102             //trace("file '%s' already seen", fname.c_str());
6103             continue;
6104             }
6105         FileRec *child  = iter->second;
6106         ofile->files[fname] = child;
6107       
6108         processDependency(ofile, child);
6109         }
6112     return true;
6119 /**
6120  *  Generate the file dependency list.
6121  */
6122 bool DepTool::generateDependencies()
6124     std::map<String, FileRec *>::iterator iter;
6125     //# First pass.  Scan for all includes
6126     for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
6127         {
6128         FileRec *frec = iter->second;
6129         if (!scanFile(iter->first, frec))
6130             {
6131             //quit?
6132             }
6133         }
6135     //# Second pass.  Scan for all includes
6136     for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
6137         {
6138         FileRec *include = iter->second;
6139         if (include->type == FileRec::CFILE)
6140             {
6141             //String cFileName   = iter->first;
6142             FileRec *ofile     = new FileRec(FileRec::OFILE);
6143             ofile->path        = include->path;
6144             ofile->baseName    = include->baseName;
6145             ofile->suffix      = include->suffix;
6146             String fname       = include->path;
6147             if (fname.size()>0)
6148                 fname.append("/");
6149             fname.append(include->baseName);
6150             fname.append(".o");
6151             oFiles[fname]    = ofile;
6152             //add the .c file first?   no, don't
6153             //ofile->files[cFileName] = include;
6154             
6155             //trace("ofile:%s", fname.c_str());
6157             processDependency(ofile, include);
6158             }
6159         }
6161       
6162     return true;
6167 /**
6168  *  High-level call to generate deps and optionally save them
6169  */
6170 bool DepTool::generateDependencies(const String &fileName)
6172     if (!createFileList())
6173         return false;
6174     if (!generateDependencies())
6175         return false;
6176     if (!saveDepFile(fileName))
6177         return false;
6178     return true;
6182 /**
6183  *   This saves the dependency cache.
6184  */
6185 bool DepTool::saveDepFile(const String &fileName)
6187     time_t tim;
6188     time(&tim);
6190     FILE *f = fopen(fileName.c_str(), "w");
6191     if (!f)
6192         {
6193         trace("cannot open '%s' for writing", fileName.c_str());
6194         }
6195     fprintf(f, "<?xml version='1.0'?>\n");
6196     fprintf(f, "<!--\n");
6197     fprintf(f, "########################################################\n");
6198     fprintf(f, "## File: build.dep\n");
6199     fprintf(f, "## Generated by BuildTool at :%s", ctime(&tim));
6200     fprintf(f, "########################################################\n");
6201     fprintf(f, "-->\n");
6203     fprintf(f, "<dependencies source='%s'>\n\n", sourceDir.c_str());
6204     std::map<String, FileRec *>::iterator iter;
6205     for (iter=oFiles.begin() ; iter!=oFiles.end() ; iter++)
6206         {
6207         FileRec *frec = iter->second;
6208         if (frec->type == FileRec::OFILE)
6209             {
6210             fprintf(f, "<object path='%s' name='%s' suffix='%s'>\n",
6211                  frec->path.c_str(), frec->baseName.c_str(), frec->suffix.c_str());
6212             std::map<String, FileRec *>::iterator citer;
6213             for (citer=frec->files.begin() ; citer!=frec->files.end() ; citer++)
6214                 {
6215                 String cfname = citer->first;
6216                 fprintf(f, "    <dep name='%s'/>\n", cfname.c_str());
6217                 }
6218             fprintf(f, "</object>\n\n");
6219             }
6220         }
6222     fprintf(f, "</dependencies>\n");
6223     fprintf(f, "\n");
6224     fprintf(f, "<!--\n");
6225     fprintf(f, "########################################################\n");
6226     fprintf(f, "## E N D\n");
6227     fprintf(f, "########################################################\n");
6228     fprintf(f, "-->\n");
6230     fclose(f);
6232     return true;
6238 /**
6239  *   This loads the dependency cache.
6240  */
6241 std::vector<DepRec> DepTool::loadDepFile(const String &depFile)
6243     std::vector<DepRec> result;
6244     
6245     Parser parser;
6246     Element *root = parser.parseFile(depFile.c_str());
6247     if (!root)
6248         {
6249         //error("Could not open %s for reading", depFile.c_str());
6250         return result;
6251         }
6253     if (root->getChildren().size()==0 ||
6254         root->getChildren()[0]->getName()!="dependencies")
6255         {
6256         error("loadDepFile: main xml element should be <dependencies>");
6257         delete root;
6258         return result;
6259         }
6261     //########## Start parsing
6262     Element *depList = root->getChildren()[0];
6264     std::vector<Element *> objects = depList->getChildren();
6265     for (unsigned int i=0 ; i<objects.size() ; i++)
6266         {
6267         Element *objectElem = objects[i];
6268         String tagName = objectElem->getName();
6269         if (tagName != "object")
6270             {
6271             error("loadDepFile: <dependencies> should have only <object> children");
6272             return result;
6273             }
6275         String objName   = objectElem->getAttribute("name");
6276          //trace("object:%s", objName.c_str());
6277         DepRec depObject(objName);
6278         depObject.path   = objectElem->getAttribute("path");
6279         depObject.suffix = objectElem->getAttribute("suffix");
6280         //########## DESCRIPTION
6281         std::vector<Element *> depElems = objectElem->getChildren();
6282         for (unsigned int i=0 ; i<depElems.size() ; i++)
6283             {
6284             Element *depElem = depElems[i];
6285             tagName = depElem->getName();
6286             if (tagName != "dep")
6287                 {
6288                 error("loadDepFile: <object> should have only <dep> children");
6289                 return result;
6290                 }
6291             String depName = depElem->getAttribute("name");
6292             //trace("    dep:%s", depName.c_str());
6293             depObject.files.push_back(depName);
6294             }
6296         //Insert into the result list, in a sorted manner
6297         bool inserted = false;
6298         std::vector<DepRec>::iterator iter;
6299         for (iter = result.begin() ; iter != result.end() ; iter++)
6300             {
6301             String vpath = iter->path;
6302             vpath.append("/");
6303             vpath.append(iter->name);
6304             String opath = depObject.path;
6305             opath.append("/");
6306             opath.append(depObject.name);
6307             if (vpath > opath)
6308                 {
6309                 inserted = true;
6310                 iter = result.insert(iter, depObject);
6311                 break;
6312                 }
6313             }
6314         if (!inserted)
6315             result.push_back(depObject);
6316         }
6318     delete root;
6320     return result;
6324 /**
6325  *   This loads the dependency cache.
6326  */
6327 std::vector<DepRec> DepTool::getDepFile(const String &depFile,
6328                    bool forceRefresh)
6330     std::vector<DepRec> result;
6331     if (forceRefresh)
6332         {
6333         generateDependencies(depFile);
6334         result = loadDepFile(depFile);
6335         }
6336     else
6337         {
6338         //try once
6339         result = loadDepFile(depFile);
6340         if (result.size() == 0)
6341             {
6342             //fail? try again
6343             generateDependencies(depFile);
6344             result = loadDepFile(depFile);
6345             }
6346         }
6347     return result;
6353 //########################################################################
6354 //# T A S K
6355 //########################################################################
6356 //forward decl
6357 class Target;
6358 class Make;
6360 /**
6361  *
6362  */
6363 class Task : public MakeBase
6366 public:
6368     typedef enum
6369         {
6370         TASK_NONE,
6371         TASK_CC,
6372         TASK_COPY,
6373         TASK_DELETE,
6374         TASK_ECHO,
6375         TASK_JAR,
6376         TASK_JAVAC,
6377         TASK_LINK,
6378         TASK_MAKEFILE,
6379         TASK_MKDIR,
6380         TASK_MSGFMT,
6381         TASK_PKG_CONFIG,
6382         TASK_RANLIB,
6383         TASK_RC,
6384         TASK_SHAREDLIB,
6385         TASK_STATICLIB,
6386         TASK_STRIP,
6387         TASK_TOUCH,
6388         TASK_TSTAMP
6389         } TaskType;
6390         
6392     /**
6393      *
6394      */
6395     Task(MakeBase &par) : parent(par)
6396         { init(); }
6398     /**
6399      *
6400      */
6401     Task(const Task &other) : parent(other.parent)
6402         { init(); assign(other); }
6404     /**
6405      *
6406      */
6407     Task &operator=(const Task &other)
6408         { assign(other); return *this; }
6410     /**
6411      *
6412      */
6413     virtual ~Task()
6414         { }
6417     /**
6418      *
6419      */
6420     virtual MakeBase &getParent()
6421         { return parent; }
6423      /**
6424      *
6425      */
6426     virtual int  getType()
6427         { return type; }
6429     /**
6430      *
6431      */
6432     virtual void setType(int val)
6433         { type = val; }
6435     /**
6436      *
6437      */
6438     virtual String getName()
6439         { return name; }
6441     /**
6442      *
6443      */
6444     virtual bool execute()
6445         { return true; }
6447     /**
6448      *
6449      */
6450     virtual bool parse(Element *elem)
6451         { return true; }
6453     /**
6454      *
6455      */
6456     Task *createTask(Element *elem, int lineNr);
6459 protected:
6461     void init()
6462         {
6463         type = TASK_NONE;
6464         name = "none";
6465         }
6467     void assign(const Task &other)
6468         {
6469         type = other.type;
6470         name = other.name;
6471         }
6472         
6473     /**
6474      *  Show task status
6475      */
6476     void taskstatus(const char *fmt, ...)
6477         {
6478         va_list args;
6479         va_start(args,fmt);
6480         fprintf(stdout, "    %s : ", name.c_str());
6481         vfprintf(stdout, fmt, args);
6482         fprintf(stdout, "\n");
6483         va_end(args) ;
6484         }
6486     String getAttribute(Element *elem, const String &attrName)
6487         {
6488         String str;
6489         return str;
6490         }
6492     MakeBase &parent;
6494     int type;
6496     String name;
6497 };
6501 /**
6502  * This task runs the C/C++ compiler.  The compiler is invoked
6503  * for all .c or .cpp files which are newer than their correcsponding
6504  * .o files.  
6505  */
6506 class TaskCC : public Task
6508 public:
6510     TaskCC(MakeBase &par) : Task(par)
6511         {
6512         type = TASK_CC;
6513         name = "cc";
6514         }
6516     virtual ~TaskCC()
6517         {}
6518         
6519     virtual bool isExcludedInc(const String &dirname)
6520         {
6521         for (unsigned int i=0 ; i<excludeInc.size() ; i++)
6522             {
6523             String fname = excludeInc[i];
6524             if (fname == dirname)
6525                 return true;
6526             }
6527         return false;
6528         }
6530     virtual bool execute()
6531         {
6532         //evaluate our parameters
6533         String command         = parent.eval(commandOpt, "gcc");
6534         String ccCommand       = parent.eval(ccCommandOpt, "gcc");
6535         String cxxCommand      = parent.eval(cxxCommandOpt, "g++");
6536         String source          = parent.eval(sourceOpt, ".");
6537         String dest            = parent.eval(destOpt, ".");
6538         String flags           = parent.eval(flagsOpt, "");
6539         String defines         = parent.eval(definesOpt, "");
6540         String includes        = parent.eval(includesOpt, "");
6541         bool continueOnError   = parent.evalBool(continueOnErrorOpt, true);
6542         bool refreshCache      = parent.evalBool(refreshCacheOpt, false);
6544         if (!listFiles(parent, fileSet))
6545             return false;
6546             
6547         FILE *f = NULL;
6548         f = fopen("compile.lst", "w");
6550         //refreshCache is probably false here, unless specified otherwise
6551         String fullName = parent.resolve("build.dep");
6552         if (refreshCache || isNewerThan(parent.getURI().getPath(), fullName))
6553             {
6554             taskstatus("regenerating C/C++ dependency cache");
6555             refreshCache = true;
6556             }
6558         DepTool depTool;
6559         depTool.setSourceDirectory(source);
6560         depTool.setFileList(fileSet.getFiles());
6561         std::vector<DepRec> deps =
6562              depTool.getDepFile("build.dep", refreshCache);
6563         
6564         String incs;
6565         incs.append("-I");
6566         incs.append(parent.resolve("."));
6567         incs.append(" ");
6568         if (includes.size()>0)
6569             {
6570             incs.append(includes);
6571             incs.append(" ");
6572             }
6573         std::set<String> paths;
6574         std::vector<DepRec>::iterator viter;
6575         for (viter=deps.begin() ; viter!=deps.end() ; viter++)
6576             {
6577             DepRec dep = *viter;
6578             if (dep.path.size()>0)
6579                 paths.insert(dep.path);
6580             }
6581         if (source.size()>0)
6582             {
6583             incs.append(" -I");
6584             incs.append(parent.resolve(source));
6585             incs.append(" ");
6586             }
6587         std::set<String>::iterator setIter;
6588         for (setIter=paths.begin() ; setIter!=paths.end() ; setIter++)
6589             {
6590             String dirName = *setIter;
6591             //check excludeInc to see if we dont want to include this dir
6592             if (isExcludedInc(dirName))
6593                 continue;
6594             incs.append(" -I");
6595             String dname;
6596             if (source.size()>0)
6597                 {
6598                 dname.append(source);
6599                 dname.append("/");
6600                 }
6601             dname.append(dirName);
6602             incs.append(parent.resolve(dname));
6603             }
6604             
6605         /**
6606          * Compile each of the C files that need it
6607          */
6608         bool errorOccurred = false;                 
6609         std::vector<String> cfiles;
6610         for (viter=deps.begin() ; viter!=deps.end() ; viter++)
6611             {
6612             DepRec dep = *viter;
6614             //## Select command
6615             String sfx = dep.suffix;
6616             String command = ccCommand;
6617             if (sfx == "cpp" || sfx == "cxx" || sfx == "c++" ||
6618                  sfx == "cc" || sfx == "CC")
6619                 command = cxxCommand;
6620  
6621             //## Make paths
6622             String destPath = dest;
6623             String srcPath  = source;
6624             if (dep.path.size()>0)
6625                 {
6626                 destPath.append("/");
6627                 destPath.append(dep.path);
6628                 srcPath.append("/");
6629                 srcPath.append(dep.path);
6630                 }
6631             //## Make sure destination directory exists
6632             if (!createDirectory(destPath))
6633                 return false;
6634                 
6635             //## Check whether it needs to be done
6636             String destName;
6637             if (destPath.size()>0)
6638                 {
6639                 destName.append(destPath);
6640                 destName.append("/");
6641                 }
6642             destName.append(dep.name);
6643             destName.append(".o");
6644             String destFullName = parent.resolve(destName);
6645             String srcName;
6646             if (srcPath.size()>0)
6647                 {
6648                 srcName.append(srcPath);
6649                 srcName.append("/");
6650                 }
6651             srcName.append(dep.name);
6652             srcName.append(".");
6653             srcName.append(dep.suffix);
6654             String srcFullName = parent.resolve(srcName);
6655             bool compileMe = false;
6656             //# First we check if the source is newer than the .o
6657             if (isNewerThan(srcFullName, destFullName))
6658                 {
6659                 taskstatus("compile of %s required by source: %s",
6660                         destFullName.c_str(), srcFullName.c_str());
6661                 compileMe = true;
6662                 }
6663             else
6664                 {
6665                 //# secondly, we check if any of the included dependencies
6666                 //# of the .c/.cpp is newer than the .o
6667                 for (unsigned int i=0 ; i<dep.files.size() ; i++)
6668                     {
6669                     String depName;
6670                     if (source.size()>0)
6671                         {
6672                         depName.append(source);
6673                         depName.append("/");
6674                         }
6675                     depName.append(dep.files[i]);
6676                     String depFullName = parent.resolve(depName);
6677                     bool depRequires = isNewerThan(depFullName, destFullName);
6678                     //trace("%d %s %s\n", depRequires,
6679                     //        destFullName.c_str(), depFullName.c_str());
6680                     if (depRequires)
6681                         {
6682                         taskstatus("compile of %s required by included: %s",
6683                                 destFullName.c_str(), depFullName.c_str());
6684                         compileMe = true;
6685                         break;
6686                         }
6687                     }
6688                 }
6689             if (!compileMe)
6690                 {
6691                 continue;
6692                 }
6694             //## Assemble the command
6695             String cmd = command;
6696             cmd.append(" -c ");
6697             cmd.append(flags);
6698             cmd.append(" ");
6699             cmd.append(defines);
6700             cmd.append(" ");
6701             cmd.append(incs);
6702             cmd.append(" ");
6703             cmd.append(srcFullName);
6704             cmd.append(" -o ");
6705             cmd.append(destFullName);
6707             //## Execute the command
6709             String outString, errString;
6710             bool ret = executeCommand(cmd.c_str(), "", outString, errString);
6712             if (f)
6713                 {
6714                 fprintf(f, "########################### File : %s\n",
6715                              srcFullName.c_str());
6716                 fprintf(f, "#### COMMAND ###\n");
6717                 int col = 0;
6718                 for (unsigned int i = 0 ; i < cmd.size() ; i++)
6719                     {
6720                     char ch = cmd[i];
6721                     if (isspace(ch)  && col > 63)
6722                         {
6723                         fputc('\n', f);
6724                         col = 0;
6725                         }
6726                     else
6727                         {
6728                         fputc(ch, f);
6729                         col++;
6730                         }
6731                     if (col > 76)
6732                         {
6733                         fputc('\n', f);
6734                         col = 0;
6735                         }
6736                     }
6737                 fprintf(f, "\n");
6738                 fprintf(f, "#### STDOUT ###\n%s\n", outString.c_str());
6739                 fprintf(f, "#### STDERR ###\n%s\n\n", errString.c_str());
6740                 fflush(f);
6741                 }
6742             if (!ret)
6743                 {
6744                 error("problem compiling: %s", errString.c_str());
6745                 errorOccurred = true;
6746                 }
6747             if (errorOccurred && !continueOnError)
6748                 break;
6749             }
6751         if (f)
6752             {
6753             fclose(f);
6754             }
6755         
6756         return !errorOccurred;
6757         }
6760     virtual bool parse(Element *elem)
6761         {
6762         String s;
6763         if (!parent.getAttribute(elem, "command", commandOpt))
6764             return false;
6765         if (commandOpt.size()>0)
6766             { cxxCommandOpt = ccCommandOpt = commandOpt; }
6767         if (!parent.getAttribute(elem, "cc", ccCommandOpt))
6768             return false;
6769         if (!parent.getAttribute(elem, "cxx", cxxCommandOpt))
6770             return false;
6771         if (!parent.getAttribute(elem, "destdir", destOpt))
6772             return false;
6773         if (!parent.getAttribute(elem, "continueOnError", continueOnErrorOpt))
6774             return false;
6775         if (!parent.getAttribute(elem, "refreshCache", refreshCacheOpt))
6776             return false;
6778         std::vector<Element *> children = elem->getChildren();
6779         for (unsigned int i=0 ; i<children.size() ; i++)
6780             {
6781             Element *child = children[i];
6782             String tagName = child->getName();
6783             if (tagName == "flags")
6784                 {
6785                 if (!parent.getValue(child, flagsOpt))
6786                     return false;
6787                 flagsOpt = strip(flagsOpt);
6788                 }
6789             else if (tagName == "includes")
6790                 {
6791                 if (!parent.getValue(child, includesOpt))
6792                     return false;
6793                 includesOpt = strip(includesOpt);
6794                 }
6795             else if (tagName == "defines")
6796                 {
6797                 if (!parent.getValue(child, definesOpt))
6798                     return false;
6799                 definesOpt = strip(definesOpt);
6800                 }
6801             else if (tagName == "fileset")
6802                 {
6803                 if (!parseFileSet(child, parent, fileSet))
6804                     return false;
6805                 sourceOpt = fileSet.getDirectory();
6806                 }
6807             else if (tagName == "excludeinc")
6808                 {
6809                 if (!parseFileList(child, parent, excludeInc))
6810                     return false;
6811                 }
6812             }
6814         return true;
6815         }
6816         
6817 protected:
6819     String   commandOpt;
6820     String   ccCommandOpt;
6821     String   cxxCommandOpt;
6822     String   sourceOpt;
6823     String   destOpt;
6824     String   flagsOpt;
6825     String   definesOpt;
6826     String   includesOpt;
6827     String   continueOnErrorOpt;
6828     String   refreshCacheOpt;
6829     FileSet  fileSet;
6830     FileList excludeInc;
6831     
6832 };
6836 /**
6837  *
6838  */
6839 class TaskCopy : public Task
6841 public:
6843     typedef enum
6844         {
6845         CP_NONE,
6846         CP_TOFILE,
6847         CP_TODIR
6848         } CopyType;
6850     TaskCopy(MakeBase &par) : Task(par)
6851         {
6852         type        = TASK_COPY;
6853         name        = "copy";
6854         cptype      = CP_NONE;
6855         haveFileSet = false;
6856         }
6858     virtual ~TaskCopy()
6859         {}
6861     virtual bool execute()
6862         {
6863         String fileName   = parent.eval(fileNameOpt   , ".");
6864         String toFileName = parent.eval(toFileNameOpt , ".");
6865         String toDirName  = parent.eval(toDirNameOpt  , ".");
6866         bool   verbose    = parent.evalBool(verboseOpt, false);
6867         switch (cptype)
6868            {
6869            case CP_TOFILE:
6870                {
6871                if (fileName.size()>0)
6872                    {
6873                    taskstatus("%s to %s",
6874                         fileName.c_str(), toFileName.c_str());
6875                    String fullSource = parent.resolve(fileName);
6876                    String fullDest = parent.resolve(toFileName);
6877                    if (verbose)
6878                        taskstatus("copy %s to file %s", fullSource.c_str(),
6879                                           fullDest.c_str());
6880                    if (!isRegularFile(fullSource))
6881                        {
6882                        error("copy : file %s does not exist", fullSource.c_str());
6883                        return false;
6884                        }
6885                    if (!isNewerThan(fullSource, fullDest))
6886                        {
6887                        taskstatus("skipped");
6888                        return true;
6889                        }
6890                    if (!copyFile(fullSource, fullDest))
6891                        return false;
6892                    taskstatus("1 file copied");
6893                    }
6894                return true;
6895                }
6896            case CP_TODIR:
6897                {
6898                if (haveFileSet)
6899                    {
6900                    if (!listFiles(parent, fileSet))
6901                        return false;
6902                    String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
6904                    taskstatus("%s to %s",
6905                        fileSetDir.c_str(), toDirName.c_str());
6907                    int nrFiles = 0;
6908                    for (unsigned int i=0 ; i<fileSet.size() ; i++)
6909                        {
6910                        String fileName = fileSet[i];
6912                        String sourcePath;
6913                        if (fileSetDir.size()>0)
6914                            {
6915                            sourcePath.append(fileSetDir);
6916                            sourcePath.append("/");
6917                            }
6918                        sourcePath.append(fileName);
6919                        String fullSource = parent.resolve(sourcePath);
6920                        
6921                        //Get the immediate parent directory's base name
6922                        String baseFileSetDir = fileSetDir;
6923                        unsigned int pos = baseFileSetDir.find_last_of('/');
6924                        if (pos!=baseFileSetDir.npos &&
6925                                   pos < baseFileSetDir.size()-1)
6926                            baseFileSetDir =
6927                               baseFileSetDir.substr(pos+1,
6928                                    baseFileSetDir.size());
6929                        //Now make the new path
6930                        String destPath;
6931                        if (toDirName.size()>0)
6932                            {
6933                            destPath.append(toDirName);
6934                            destPath.append("/");
6935                            }
6936                        if (baseFileSetDir.size()>0)
6937                            {
6938                            destPath.append(baseFileSetDir);
6939                            destPath.append("/");
6940                            }
6941                        destPath.append(fileName);
6942                        String fullDest = parent.resolve(destPath);
6943                        //trace("fileName:%s", fileName.c_str());
6944                        if (verbose)
6945                            taskstatus("copy %s to new dir : %s",
6946                                  fullSource.c_str(), fullDest.c_str());
6947                        if (!isNewerThan(fullSource, fullDest))
6948                            {
6949                            if (verbose)
6950                                taskstatus("copy skipping %s", fullSource.c_str());
6951                            continue;
6952                            }
6953                        if (!copyFile(fullSource, fullDest))
6954                            return false;
6955                        nrFiles++;
6956                        }
6957                    taskstatus("%d file(s) copied", nrFiles);
6958                    }
6959                else //file source
6960                    {
6961                    //For file->dir we want only the basename of
6962                    //the source appended to the dest dir
6963                    taskstatus("%s to %s", 
6964                        fileName.c_str(), toDirName.c_str());
6965                    String baseName = fileName;
6966                    unsigned int pos = baseName.find_last_of('/');
6967                    if (pos!=baseName.npos && pos<baseName.size()-1)
6968                        baseName = baseName.substr(pos+1, baseName.size());
6969                    String fullSource = parent.resolve(fileName);
6970                    String destPath;
6971                    if (toDirName.size()>0)
6972                        {
6973                        destPath.append(toDirName);
6974                        destPath.append("/");
6975                        }
6976                    destPath.append(baseName);
6977                    String fullDest = parent.resolve(destPath);
6978                    if (verbose)
6979                        taskstatus("file %s to new dir : %s", fullSource.c_str(),
6980                                           fullDest.c_str());
6981                    if (!isRegularFile(fullSource))
6982                        {
6983                        error("copy : file %s does not exist", fullSource.c_str());
6984                        return false;
6985                        }
6986                    if (!isNewerThan(fullSource, fullDest))
6987                        {
6988                        taskstatus("skipped");
6989                        return true;
6990                        }
6991                    if (!copyFile(fullSource, fullDest))
6992                        return false;
6993                    taskstatus("1 file copied");
6994                    }
6995                return true;
6996                }
6997            }
6998         return true;
6999         }
7002     virtual bool parse(Element *elem)
7003         {
7004         if (!parent.getAttribute(elem, "file", fileNameOpt))
7005             return false;
7006         if (!parent.getAttribute(elem, "tofile", toFileNameOpt))
7007             return false;
7008         if (toFileNameOpt.size() > 0)
7009             cptype = CP_TOFILE;
7010         if (!parent.getAttribute(elem, "todir", toDirNameOpt))
7011             return false;
7012         if (toDirNameOpt.size() > 0)
7013             cptype = CP_TODIR;
7014         if (!parent.getAttribute(elem, "verbose", verboseOpt))
7015             return false;
7016             
7017         haveFileSet = false;
7018         
7019         std::vector<Element *> children = elem->getChildren();
7020         for (unsigned int i=0 ; i<children.size() ; i++)
7021             {
7022             Element *child = children[i];
7023             String tagName = child->getName();
7024             if (tagName == "fileset")
7025                 {
7026                 if (!parseFileSet(child, parent, fileSet))
7027                     {
7028                     error("problem getting fileset");
7029                     return false;
7030                     }
7031                 haveFileSet = true;
7032                 }
7033             }
7035         //Perform validity checks
7036         if (fileNameOpt.size()>0 && fileSet.size()>0)
7037             {
7038             error("<copy> can only have one of : file= and <fileset>");
7039             return false;
7040             }
7041         if (toFileNameOpt.size()>0 && toDirNameOpt.size()>0)
7042             {
7043             error("<copy> can only have one of : tofile= or todir=");
7044             return false;
7045             }
7046         if (haveFileSet && toDirNameOpt.size()==0)
7047             {
7048             error("a <copy> task with a <fileset> must have : todir=");
7049             return false;
7050             }
7051         if (cptype == CP_TOFILE && fileNameOpt.size()==0)
7052             {
7053             error("<copy> tofile= must be associated with : file=");
7054             return false;
7055             }
7056         if (cptype == CP_TODIR && fileNameOpt.size()==0 && !haveFileSet)
7057             {
7058             error("<copy> todir= must be associated with : file= or <fileset>");
7059             return false;
7060             }
7062         return true;
7063         }
7064         
7065 private:
7067     int cptype;
7068     bool haveFileSet;
7070     FileSet fileSet;
7071     String  fileNameOpt;
7072     String  toFileNameOpt;
7073     String  toDirNameOpt;
7074     String  verboseOpt;
7075 };
7078 /**
7079  *
7080  */
7081 class TaskDelete : public Task
7083 public:
7085     typedef enum
7086         {
7087         DEL_FILE,
7088         DEL_DIR,
7089         DEL_FILESET
7090         } DeleteType;
7092     TaskDelete(MakeBase &par) : Task(par)
7093         { 
7094         type        = TASK_DELETE;
7095         name        = "delete";
7096         delType     = DEL_FILE;
7097         }
7099     virtual ~TaskDelete()
7100         {}
7102     virtual bool execute()
7103         {
7104         String dirName   = parent.eval(dirNameOpt, ".");
7105         String fileName  = parent.eval(fileNameOpt, ".");
7106         bool verbose     = parent.evalBool(verboseOpt, false);
7107         bool quiet       = parent.evalBool(quietOpt, false);
7108         bool failOnError = parent.evalBool(failOnErrorOpt, true);
7109         struct stat finfo;
7110         switch (delType)
7111             {
7112             case DEL_FILE:
7113                 {
7114                 taskstatus("file: %s", fileName.c_str());
7115                 String fullName = parent.resolve(fileName);
7116                 char *fname = (char *)fullName.c_str();
7117                 if (!quiet && verbose)
7118                     taskstatus("path: %s", fname);
7119                 //does not exist
7120                 if (stat(fname, &finfo)<0)
7121                     {
7122                     if (failOnError)
7123                         return false;
7124                     else
7125                         return true;
7126                     }
7127                 //exists but is not a regular file
7128                 if (!S_ISREG(finfo.st_mode))
7129                     {
7130                     error("<delete> failed. '%s' exists and is not a regular file",
7131                           fname);
7132                     return false;
7133                     }
7134                 if (remove(fname)<0)
7135                     {
7136                     error("<delete> failed: %s", strerror(errno));
7137                     return false;
7138                     }
7139                 return true;
7140                 }
7141             case DEL_DIR:
7142                 {
7143                 taskstatus("dir: %s", dirName.c_str());
7144                 String fullDir = parent.resolve(dirName);
7145                 if (!quiet && verbose)
7146                     taskstatus("path: %s", fullDir.c_str());
7147                 if (!removeDirectory(fullDir))
7148                     return false;
7149                 return true;
7150                 }
7151             }
7152         return true;
7153         }
7155     virtual bool parse(Element *elem)
7156         {
7157         if (!parent.getAttribute(elem, "file", fileNameOpt))
7158             return false;
7159         if (fileNameOpt.size() > 0)
7160             delType = DEL_FILE;
7161         if (!parent.getAttribute(elem, "dir", dirNameOpt))
7162             return false;
7163         if (dirNameOpt.size() > 0)
7164             delType = DEL_DIR;
7165         if (fileNameOpt.size()>0 && dirNameOpt.size()>0)
7166             {
7167             error("<delete> can have one attribute of file= or dir=");
7168             return false;
7169             }
7170         if (fileNameOpt.size()==0 && dirNameOpt.size()==0)
7171             {
7172             error("<delete> must have one attribute of file= or dir=");
7173             return false;
7174             }
7175         if (!parent.getAttribute(elem, "verbose", verboseOpt))
7176             return false;
7177         if (!parent.getAttribute(elem, "quiet", quietOpt))
7178             return false;
7179         if (!parent.getAttribute(elem, "failonerror", failOnErrorOpt))
7180             return false;
7181         return true;
7182         }
7184 private:
7186     int delType;
7187     String dirNameOpt;
7188     String fileNameOpt;
7189     String verboseOpt;
7190     String quietOpt;
7191     String failOnErrorOpt;
7192 };
7195 /**
7196  * Send a message to stdout
7197  */
7198 class TaskEcho : public Task
7200 public:
7202     TaskEcho(MakeBase &par) : Task(par)
7203         { type = TASK_ECHO; name = "echo"; }
7205     virtual ~TaskEcho()
7206         {}
7208     virtual bool execute()
7209         {
7210         //let message have priority over text
7211         String message = parent.eval(messageOpt, "");
7212         String text    = parent.eval(textOpt, "");
7213         if (message.size() > 0)
7214             {
7215             fprintf(stdout, "%s\n", message.c_str());
7216             }
7217         else if (text.size() > 0)
7218             {
7219             fprintf(stdout, "%s\n", text.c_str());
7220             }
7221         return true;
7222         }
7224     virtual bool parse(Element *elem)
7225         {
7226         if (!parent.getValue(elem, textOpt))
7227             return false;
7228         textOpt    = leftJustify(textOpt);
7229         if (!parent.getAttribute(elem, "message", messageOpt))
7230             return false;
7231         return true;
7232         }
7234 private:
7236     String messageOpt;
7237     String textOpt;
7238 };
7242 /**
7243  *
7244  */
7245 class TaskJar : public Task
7247 public:
7249     TaskJar(MakeBase &par) : Task(par)
7250         { type = TASK_JAR; name = "jar"; }
7252     virtual ~TaskJar()
7253         {}
7255     virtual bool execute()
7256         {
7257         String command  = parent.eval(commandOpt, "jar");
7258         String basedir  = parent.eval(basedirOpt, ".");
7259         String destfile = parent.eval(destfileOpt, ".");
7261         String cmd = command;
7262         cmd.append(" -cf ");
7263         cmd.append(destfile);
7264         cmd.append(" -C ");
7265         cmd.append(basedir);
7266         cmd.append(" .");
7268         String execCmd = cmd;
7270         String outString, errString;
7271         bool ret = executeCommand(execCmd.c_str(), "", outString, errString);
7272         if (!ret)
7273             {
7274             error("<jar> command '%s' failed :\n %s",
7275                                       execCmd.c_str(), errString.c_str());
7276             return false;
7277             }
7278         return true;
7279         }
7281     virtual bool parse(Element *elem)
7282         {
7283         if (!parent.getAttribute(elem, "command", commandOpt))
7284             return false;
7285         if (!parent.getAttribute(elem, "basedir", basedirOpt))
7286             return false;
7287         if (!parent.getAttribute(elem, "destfile", destfileOpt))
7288             return false;
7289         if (basedirOpt.size() == 0 || destfileOpt.size() == 0)
7290             {
7291             error("<jar> required both basedir and destfile attributes to be set");
7292             return false;
7293             }
7294         return true;
7295         }
7297 private:
7299     String commandOpt;
7300     String basedirOpt;
7301     String destfileOpt;
7302 };
7305 /**
7306  *
7307  */
7308 class TaskJavac : public Task
7310 public:
7312     TaskJavac(MakeBase &par) : Task(par)
7313         { 
7314         type = TASK_JAVAC; name = "javac";
7315         }
7317     virtual ~TaskJavac()
7318         {}
7320     virtual bool execute()
7321         {
7322         String command  = parent.eval(commandOpt, "javac");
7323         String srcdir   = parent.eval(srcdirOpt, ".");
7324         String destdir  = parent.eval(destdirOpt, ".");
7325         String target   = parent.eval(targetOpt, "");
7327         std::vector<String> fileList;
7328         if (!listFiles(srcdir, "", fileList))
7329             {
7330             return false;
7331             }
7332         String cmd = command;
7333         cmd.append(" -d ");
7334         cmd.append(destdir);
7335         cmd.append(" -classpath ");
7336         cmd.append(destdir);
7337         cmd.append(" -sourcepath ");
7338         cmd.append(srcdir);
7339         cmd.append(" ");
7340         if (target.size()>0)
7341             {
7342             cmd.append(" -target ");
7343             cmd.append(target);
7344             cmd.append(" ");
7345             }
7346         String fname = "javalist.btool";
7347         FILE *f = fopen(fname.c_str(), "w");
7348         int count = 0;
7349         for (unsigned int i=0 ; i<fileList.size() ; i++)
7350             {
7351             String fname = fileList[i];
7352             String srcName = fname;
7353             if (fname.size()<6) //x.java
7354                 continue;
7355             if (fname.compare(fname.size()-5, 5, ".java") != 0)
7356                 continue;
7357             String baseName = fname.substr(0, fname.size()-5);
7358             String destName = baseName;
7359             destName.append(".class");
7361             String fullSrc = srcdir;
7362             fullSrc.append("/");
7363             fullSrc.append(fname);
7364             String fullDest = destdir;
7365             fullDest.append("/");
7366             fullDest.append(destName);
7367             //trace("fullsrc:%s fulldest:%s", fullSrc.c_str(), fullDest.c_str());
7368             if (!isNewerThan(fullSrc, fullDest))
7369                 continue;
7371             count++;
7372             fprintf(f, "%s\n", fullSrc.c_str());
7373             }
7374         fclose(f);
7375         if (!count)
7376             {
7377             taskstatus("nothing to do");
7378             return true;
7379             }
7381         taskstatus("compiling %d files", count);
7383         String execCmd = cmd;
7384         execCmd.append("@");
7385         execCmd.append(fname);
7387         String outString, errString;
7388         bool ret = executeCommand(execCmd.c_str(), "", outString, errString);
7389         if (!ret)
7390             {
7391             error("<javac> command '%s' failed :\n %s",
7392                                       execCmd.c_str(), errString.c_str());
7393             return false;
7394             }
7395         return true;
7396         }
7398     virtual bool parse(Element *elem)
7399         {
7400         if (!parent.getAttribute(elem, "command", commandOpt))
7401             return false;
7402         if (!parent.getAttribute(elem, "srcdir", srcdirOpt))
7403             return false;
7404         if (!parent.getAttribute(elem, "destdir", destdirOpt))
7405             return false;
7406         if (srcdirOpt.size() == 0 || destdirOpt.size() == 0)
7407             {
7408             error("<javac> required both srcdir and destdir attributes to be set");
7409             return false;
7410             }
7411         if (!parent.getAttribute(elem, "target", targetOpt))
7412             return false;
7413         return true;
7414         }
7416 private:
7418     String commandOpt;
7419     String srcdirOpt;
7420     String destdirOpt;
7421     String targetOpt;
7423 };
7426 /**
7427  *
7428  */
7429 class TaskLink : public Task
7431 public:
7433     TaskLink(MakeBase &par) : Task(par)
7434         {
7435         type = TASK_LINK; name = "link";
7436         }
7438     virtual ~TaskLink()
7439         {}
7441     virtual bool execute()
7442         {
7443         String  command        = parent.eval(commandOpt, "g++");
7444         String  fileName       = parent.eval(fileNameOpt, "");
7445         String  flags          = parent.eval(flagsOpt, "");
7446         String  libs           = parent.eval(libsOpt, "");
7447         bool    doStrip        = parent.evalBool(doStripOpt, false);
7448         String  symFileName    = parent.eval(symFileNameOpt, "");
7449         String  stripCommand   = parent.eval(stripCommandOpt, "strip");
7450         String  objcopyCommand = parent.eval(objcopyCommandOpt, "objcopy");
7452         if (!listFiles(parent, fileSet))
7453             return false;
7454         String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
7455         //trace("%d files in %s", fileSet.size(), fileSetDir.c_str());
7456         bool doit = false;
7457         String fullTarget = parent.resolve(fileName);
7458         String cmd = command;
7459         cmd.append(" -o ");
7460         cmd.append(fullTarget);
7461         cmd.append(" ");
7462         cmd.append(flags);
7463         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7464             {
7465             cmd.append(" ");
7466             String obj;
7467             if (fileSetDir.size()>0)
7468                 {
7469                 obj.append(fileSetDir);
7470                 obj.append("/");
7471                 }
7472             obj.append(fileSet[i]);
7473             String fullObj = parent.resolve(obj);
7474             String nativeFullObj = getNativePath(fullObj);
7475             cmd.append(nativeFullObj);
7476             //trace("link: tgt:%s obj:%s", fullTarget.c_str(),
7477             //          fullObj.c_str());
7478             if (isNewerThan(fullObj, fullTarget))
7479                 doit = true;
7480             }
7481         cmd.append(" ");
7482         cmd.append(libs);
7483         if (!doit)
7484             {
7485             //trace("link not needed");
7486             return true;
7487             }
7488         //trace("LINK cmd:%s", cmd.c_str());
7491         String outbuf, errbuf;
7492         if (!executeCommand(cmd.c_str(), "", outbuf, errbuf))
7493             {
7494             error("LINK problem: %s", errbuf.c_str());
7495             return false;
7496             }
7498         if (symFileName.size()>0)
7499             {
7500             String symFullName = parent.resolve(symFileName);
7501             cmd = objcopyCommand;
7502             cmd.append(" --only-keep-debug ");
7503             cmd.append(getNativePath(fullTarget));
7504             cmd.append(" ");
7505             cmd.append(getNativePath(symFullName));
7506             if (!executeCommand(cmd, "", outbuf, errbuf))
7507                 {
7508                 error("<strip> symbol file failed : %s", errbuf.c_str());
7509                 return false;
7510                 }
7511             }
7512             
7513         if (doStrip)
7514             {
7515             cmd = stripCommand;
7516             cmd.append(" ");
7517             cmd.append(getNativePath(fullTarget));
7518             if (!executeCommand(cmd, "", outbuf, errbuf))
7519                {
7520                error("<strip> failed : %s", errbuf.c_str());
7521                return false;
7522                }
7523             }
7525         return true;
7526         }
7528     virtual bool parse(Element *elem)
7529         {
7530         if (!parent.getAttribute(elem, "command", commandOpt))
7531             return false;
7532         if (!parent.getAttribute(elem, "objcopycommand", objcopyCommandOpt))
7533             return false;
7534         if (!parent.getAttribute(elem, "stripcommand", stripCommandOpt))
7535             return false;
7536         if (!parent.getAttribute(elem, "out", fileNameOpt))
7537             return false;
7538         if (!parent.getAttribute(elem, "strip", doStripOpt))
7539             return false;
7540         if (!parent.getAttribute(elem, "symfile", symFileNameOpt))
7541             return false;
7542             
7543         std::vector<Element *> children = elem->getChildren();
7544         for (unsigned int i=0 ; i<children.size() ; i++)
7545             {
7546             Element *child = children[i];
7547             String tagName = child->getName();
7548             if (tagName == "fileset")
7549                 {
7550                 if (!parseFileSet(child, parent, fileSet))
7551                     return false;
7552                 }
7553             else if (tagName == "flags")
7554                 {
7555                 if (!parent.getValue(child, flagsOpt))
7556                     return false;
7557                 flagsOpt = strip(flagsOpt);
7558                 }
7559             else if (tagName == "libs")
7560                 {
7561                 if (!parent.getValue(child, libsOpt))
7562                     return false;
7563                 libsOpt = strip(libsOpt);
7564                 }
7565             }
7566         return true;
7567         }
7569 private:
7571     FileSet fileSet;
7573     String  commandOpt;
7574     String  fileNameOpt;
7575     String  flagsOpt;
7576     String  libsOpt;
7577     String  doStripOpt;
7578     String  symFileNameOpt;
7579     String  stripCommandOpt;
7580     String  objcopyCommandOpt;
7582 };
7586 /**
7587  * Create a named file
7588  */
7589 class TaskMakeFile : public Task
7591 public:
7593     TaskMakeFile(MakeBase &par) : Task(par)
7594         { type = TASK_MAKEFILE; name = "makefile"; }
7596     virtual ~TaskMakeFile()
7597         {}
7599     virtual bool execute()
7600         {
7601         String fileName = parent.eval(fileNameOpt, "");
7602         String text     = parent.eval(textOpt, "");
7604         taskstatus("%s", fileName.c_str());
7605         String fullName = parent.resolve(fileName);
7606         if (!isNewerThan(parent.getURI().getPath(), fullName))
7607             {
7608             //trace("skipped <makefile>");
7609             return true;
7610             }
7611         String fullNative = getNativePath(fullName);
7612         //trace("fullName:%s", fullName.c_str());
7613         FILE *f = fopen(fullNative.c_str(), "w");
7614         if (!f)
7615             {
7616             error("<makefile> could not open %s for writing : %s",
7617                 fullName.c_str(), strerror(errno));
7618             return false;
7619             }
7620         for (unsigned int i=0 ; i<text.size() ; i++)
7621             fputc(text[i], f);
7622         fputc('\n', f);
7623         fclose(f);
7624         return true;
7625         }
7627     virtual bool parse(Element *elem)
7628         {
7629         if (!parent.getAttribute(elem, "file", fileNameOpt))
7630             return false;
7631         if (fileNameOpt.size() == 0)
7632             {
7633             error("<makefile> requires 'file=\"filename\"' attribute");
7634             return false;
7635             }
7636         if (!parent.getValue(elem, textOpt))
7637             return false;
7638         textOpt = leftJustify(textOpt);
7639         //trace("dirname:%s", dirName.c_str());
7640         return true;
7641         }
7643 private:
7645     String fileNameOpt;
7646     String textOpt;
7647 };
7651 /**
7652  * Create a named directory
7653  */
7654 class TaskMkDir : public Task
7656 public:
7658     TaskMkDir(MakeBase &par) : Task(par)
7659         { type = TASK_MKDIR; name = "mkdir"; }
7661     virtual ~TaskMkDir()
7662         {}
7664     virtual bool execute()
7665         {
7666         String dirName = parent.eval(dirNameOpt, ".");
7667         
7668         taskstatus("%s", dirName.c_str());
7669         String fullDir = parent.resolve(dirName);
7670         //trace("fullDir:%s", fullDir.c_str());
7671         if (!createDirectory(fullDir))
7672             return false;
7673         return true;
7674         }
7676     virtual bool parse(Element *elem)
7677         {
7678         if (!parent.getAttribute(elem, "dir", dirNameOpt))
7679             return false;
7680         if (dirNameOpt.size() == 0)
7681             {
7682             error("<mkdir> requires 'dir=\"dirname\"' attribute");
7683             return false;
7684             }
7685         return true;
7686         }
7688 private:
7690     String dirNameOpt;
7691 };
7695 /**
7696  * Create a named directory
7697  */
7698 class TaskMsgFmt: public Task
7700 public:
7702     TaskMsgFmt(MakeBase &par) : Task(par)
7703          { type = TASK_MSGFMT;  name = "msgfmt"; }
7705     virtual ~TaskMsgFmt()
7706         {}
7708     virtual bool execute()
7709         {
7710         String  command   = parent.eval(commandOpt, "msgfmt");
7711         String  toDirName = parent.eval(toDirNameOpt, ".");
7712         String  outName   = parent.eval(outNameOpt, "");
7713         bool    owndir    = parent.evalBool(owndirOpt, false);
7715         if (!listFiles(parent, fileSet))
7716             return false;
7717         String fileSetDir = fileSet.getDirectory();
7719         //trace("msgfmt: %d", fileSet.size());
7720         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7721             {
7722             String fileName = fileSet[i];
7723             if (getSuffix(fileName) != "po")
7724                 continue;
7725             String sourcePath;
7726             if (fileSetDir.size()>0)
7727                 {
7728                 sourcePath.append(fileSetDir);
7729                 sourcePath.append("/");
7730                 }
7731             sourcePath.append(fileName);
7732             String fullSource = parent.resolve(sourcePath);
7734             String destPath;
7735             if (toDirName.size()>0)
7736                 {
7737                 destPath.append(toDirName);
7738                 destPath.append("/");
7739                 }
7740             if (owndir)
7741                 {
7742                 String subdir = fileName;
7743                 unsigned int pos = subdir.find_last_of('.');
7744                 if (pos != subdir.npos)
7745                     subdir = subdir.substr(0, pos);
7746                 destPath.append(subdir);
7747                 destPath.append("/");
7748                 }
7749             //Pick the output file name
7750             if (outName.size() > 0)
7751                 {
7752                 destPath.append(outName);
7753                 }
7754             else
7755                 {
7756                 destPath.append(fileName);
7757                 destPath[destPath.size()-2] = 'm';
7758                 }
7760             String fullDest = parent.resolve(destPath);
7762             if (!isNewerThan(fullSource, fullDest))
7763                 {
7764                 //trace("skip %s", fullSource.c_str());
7765                 continue;
7766                 }
7767                 
7768             String cmd = command;
7769             cmd.append(" ");
7770             cmd.append(fullSource);
7771             cmd.append(" -o ");
7772             cmd.append(fullDest);
7773             
7774             int pos = fullDest.find_last_of('/');
7775             if (pos>0)
7776                 {
7777                 String fullDestPath = fullDest.substr(0, pos);
7778                 if (!createDirectory(fullDestPath))
7779                     return false;
7780                 }
7784             String outString, errString;
7785             if (!executeCommand(cmd.c_str(), "", outString, errString))
7786                 {
7787                 error("<msgfmt> problem: %s", errString.c_str());
7788                 return false;
7789                 }
7790             }
7792         return true;
7793         }
7795     virtual bool parse(Element *elem)
7796         {
7797         if (!parent.getAttribute(elem, "command", commandOpt))
7798             return false;
7799         if (!parent.getAttribute(elem, "todir", toDirNameOpt))
7800             return false;
7801         if (!parent.getAttribute(elem, "out", outNameOpt))
7802             return false;
7803         if (!parent.getAttribute(elem, "owndir", owndirOpt))
7804             return false;
7805             
7806         std::vector<Element *> children = elem->getChildren();
7807         for (unsigned int i=0 ; i<children.size() ; i++)
7808             {
7809             Element *child = children[i];
7810             String tagName = child->getName();
7811             if (tagName == "fileset")
7812                 {
7813                 if (!parseFileSet(child, parent, fileSet))
7814                     return false;
7815                 }
7816             }
7817         return true;
7818         }
7820 private:
7822     FileSet fileSet;
7824     String  commandOpt;
7825     String  toDirNameOpt;
7826     String  outNameOpt;
7827     String  owndirOpt;
7829 };
7833 /**
7834  *  Perform a Package-Config query similar to pkg-config
7835  */
7836 class TaskPkgConfig : public Task
7838 public:
7840     typedef enum
7841         {
7842         PKG_CONFIG_QUERY_CFLAGS,
7843         PKG_CONFIG_QUERY_LIBS,
7844         PKG_CONFIG_QUERY_ALL
7845         } QueryTypes;
7847     TaskPkgConfig(MakeBase &par) : Task(par)
7848         {
7849         type = TASK_PKG_CONFIG;
7850         name = "pkg-config";
7851         }
7853     virtual ~TaskPkgConfig()
7854         {}
7856     virtual bool execute()
7857         {
7858         String pkgName       = parent.eval(pkgNameOpt,      "");
7859         String prefix        = parent.eval(prefixOpt,       "");
7860         String propName      = parent.eval(propNameOpt,     "");
7861         String pkgConfigPath = parent.eval(pkgConfigPathOpt,"");
7862         String query         = parent.eval(queryOpt,        "all");
7864         String path = parent.resolve(pkgConfigPath);
7865         PkgConfig pkgconfig;
7866         pkgconfig.setPath(path);
7867         pkgconfig.setPrefix(prefix);
7868         if (!pkgconfig.query(pkgName))
7869             {
7870             error("<pkg-config> query failed for '%s", name.c_str());
7871             return false;
7872             }
7873             
7874         String val = "";
7875         if (query == "cflags")
7876             val = pkgconfig.getCflags();
7877         else if (query == "libs")
7878             val =pkgconfig.getLibs();
7879         else if (query == "all")
7880             val = pkgconfig.getAll();
7881         else
7882             {
7883             error("<pkg-config> unhandled query : %s", query.c_str());
7884             return false;
7885             }
7886         taskstatus("property %s = '%s'", propName.c_str(), val.c_str());
7887         parent.setProperty(propName, val);
7888         return true;
7889         }
7891     virtual bool parse(Element *elem)
7892         {
7893         //# NAME
7894         if (!parent.getAttribute(elem, "name", pkgNameOpt))
7895             return false;
7896         if (pkgNameOpt.size()==0)
7897             {
7898             error("<pkg-config> requires 'name=\"package\"' attribute");
7899             return false;
7900             }
7902         //# PROPERTY
7903         if (!parent.getAttribute(elem, "property", propNameOpt))
7904             return false;
7905         if (propNameOpt.size()==0)
7906             {
7907             error("<pkg-config> requires 'property=\"name\"' attribute");
7908             return false;
7909             }
7910         //# PATH
7911         if (!parent.getAttribute(elem, "path", pkgConfigPathOpt))
7912             return false;
7913         //# PREFIX
7914         if (!parent.getAttribute(elem, "prefix", prefixOpt))
7915             return false;
7916         //# QUERY
7917         if (!parent.getAttribute(elem, "query", queryOpt))
7918             return false;
7920         return true;
7921         }
7923 private:
7925     String queryOpt;
7926     String pkgNameOpt;
7927     String prefixOpt;
7928     String propNameOpt;
7929     String pkgConfigPathOpt;
7931 };
7938 /**
7939  *  Process an archive to allow random access
7940  */
7941 class TaskRanlib : public Task
7943 public:
7945     TaskRanlib(MakeBase &par) : Task(par)
7946         { type = TASK_RANLIB; name = "ranlib"; }
7948     virtual ~TaskRanlib()
7949         {}
7951     virtual bool execute()
7952         {
7953         String fileName = parent.eval(fileNameOpt, "");
7954         String command  = parent.eval(commandOpt, "ranlib");
7956         String fullName = parent.resolve(fileName);
7957         //trace("fullDir:%s", fullDir.c_str());
7958         String cmd = command;
7959         cmd.append(" ");
7960         cmd.append(fullName);
7961         String outbuf, errbuf;
7962         if (!executeCommand(cmd, "", outbuf, errbuf))
7963             return false;
7964         return true;
7965         }
7967     virtual bool parse(Element *elem)
7968         {
7969         if (!parent.getAttribute(elem, "command", commandOpt))
7970             return false;
7971         if (!parent.getAttribute(elem, "file", fileNameOpt))
7972             return false;
7973         if (fileNameOpt.size() == 0)
7974             {
7975             error("<ranlib> requires 'file=\"fileNname\"' attribute");
7976             return false;
7977             }
7978         return true;
7979         }
7981 private:
7983     String fileNameOpt;
7984     String commandOpt;
7985 };
7989 /**
7990  * Compile a resource file into a binary object
7991  */
7992 class TaskRC : public Task
7994 public:
7996     TaskRC(MakeBase &par) : Task(par)
7997         { type = TASK_RC; name = "rc"; }
7999     virtual ~TaskRC()
8000         {}
8002     virtual bool execute()
8003         {
8004         String command  = parent.eval(commandOpt,  "windres");
8005         String flags    = parent.eval(flagsOpt,    "");
8006         String fileName = parent.eval(fileNameOpt, "");
8007         String outName  = parent.eval(outNameOpt,  "");
8009         String fullFile = parent.resolve(fileName);
8010         String fullOut  = parent.resolve(outName);
8011         if (!isNewerThan(fullFile, fullOut))
8012             return true;
8013         String cmd = command;
8014         cmd.append(" -o ");
8015         cmd.append(fullOut);
8016         cmd.append(" ");
8017         cmd.append(flags);
8018         cmd.append(" ");
8019         cmd.append(fullFile);
8021         String outString, errString;
8022         if (!executeCommand(cmd.c_str(), "", outString, errString))
8023             {
8024             error("RC problem: %s", errString.c_str());
8025             return false;
8026             }
8027         return true;
8028         }
8030     virtual bool parse(Element *elem)
8031         {
8032         if (!parent.getAttribute(elem, "command", commandOpt))
8033             return false;
8034         if (!parent.getAttribute(elem, "file", fileNameOpt))
8035             return false;
8036         if (!parent.getAttribute(elem, "out", outNameOpt))
8037             return false;
8038         std::vector<Element *> children = elem->getChildren();
8039         for (unsigned int i=0 ; i<children.size() ; i++)
8040             {
8041             Element *child = children[i];
8042             String tagName = child->getName();
8043             if (tagName == "flags")
8044                 {
8045                 if (!parent.getValue(child, flagsOpt))
8046                     return false;
8047                 }
8048             }
8049         return true;
8050         }
8052 private:
8054     String commandOpt;
8055     String flagsOpt;
8056     String fileNameOpt;
8057     String outNameOpt;
8059 };
8063 /**
8064  *  Collect .o's into a .so or DLL
8065  */
8066 class TaskSharedLib : public Task
8068 public:
8070     TaskSharedLib(MakeBase &par) : Task(par)
8071         { type = TASK_SHAREDLIB; name = "dll"; }
8073     virtual ~TaskSharedLib()
8074         {}
8076     virtual bool execute()
8077         {
8078         String command     = parent.eval(commandOpt, "dllwrap");
8079         String fileName    = parent.eval(fileNameOpt, "");
8080         String defFileName = parent.eval(defFileNameOpt, "");
8081         String impFileName = parent.eval(impFileNameOpt, "");
8082         String libs        = parent.eval(libsOpt, "");
8084         //trace("###########HERE %d", fileSet.size());
8085         bool doit = false;
8086         
8087         String fullOut = parent.resolve(fileName);
8088         //trace("ar fullout: %s", fullOut.c_str());
8089         
8090         if (!listFiles(parent, fileSet))
8091             return false;
8092         String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
8094         for (unsigned int i=0 ; i<fileSet.size() ; i++)
8095             {
8096             String fname;
8097             if (fileSetDir.size()>0)
8098                 {
8099                 fname.append(fileSetDir);
8100                 fname.append("/");
8101                 }
8102             fname.append(fileSet[i]);
8103             String fullName = parent.resolve(fname);
8104             //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
8105             if (isNewerThan(fullName, fullOut))
8106                 doit = true;
8107             }
8108         //trace("Needs it:%d", doit);
8109         if (!doit)
8110             {
8111             return true;
8112             }
8114         String cmd = "dllwrap";
8115         cmd.append(" -o ");
8116         cmd.append(fullOut);
8117         if (defFileName.size()>0)
8118             {
8119             cmd.append(" --def ");
8120             cmd.append(defFileName);
8121             cmd.append(" ");
8122             }
8123         if (impFileName.size()>0)
8124             {
8125             cmd.append(" --implib ");
8126             cmd.append(impFileName);
8127             cmd.append(" ");
8128             }
8129         for (unsigned int i=0 ; i<fileSet.size() ; i++)
8130             {
8131             String fname;
8132             if (fileSetDir.size()>0)
8133                 {
8134                 fname.append(fileSetDir);
8135                 fname.append("/");
8136                 }
8137             fname.append(fileSet[i]);
8138             String fullName = parent.resolve(fname);
8140             cmd.append(" ");
8141             cmd.append(fullName);
8142             }
8143         cmd.append(" ");
8144         cmd.append(libs);
8146         String outString, errString;
8147         if (!executeCommand(cmd.c_str(), "", outString, errString))
8148             {
8149             error("<sharedlib> problem: %s", errString.c_str());
8150             return false;
8151             }
8153         return true;
8154         }
8156     virtual bool parse(Element *elem)
8157         {
8158         if (!parent.getAttribute(elem, "command", commandOpt))
8159             return false;
8160         if (!parent.getAttribute(elem, "file", fileNameOpt))
8161             return false;
8162         if (!parent.getAttribute(elem, "import", impFileNameOpt))
8163             return false;
8164         if (!parent.getAttribute(elem, "def", defFileNameOpt))
8165             return false;
8166             
8167         std::vector<Element *> children = elem->getChildren();
8168         for (unsigned int i=0 ; i<children.size() ; i++)
8169             {
8170             Element *child = children[i];
8171             String tagName = child->getName();
8172             if (tagName == "fileset")
8173                 {
8174                 if (!parseFileSet(child, parent, fileSet))
8175                     return false;
8176                 }
8177             else if (tagName == "libs")
8178                 {
8179                 if (!parent.getValue(child, libsOpt))
8180                     return false;
8181                 libsOpt = strip(libsOpt);
8182                 }
8183             }
8184         return true;
8185         }
8187 private:
8189     FileSet fileSet;
8191     String commandOpt;
8192     String fileNameOpt;
8193     String defFileNameOpt;
8194     String impFileNameOpt;
8195     String libsOpt;
8197 };
8201 /**
8202  * Run the "ar" command to archive .o's into a .a
8203  */
8204 class TaskStaticLib : public Task
8206 public:
8208     TaskStaticLib(MakeBase &par) : Task(par)
8209         { type = TASK_STATICLIB; name = "staticlib"; }
8211     virtual ~TaskStaticLib()
8212         {}
8214     virtual bool execute()
8215         {
8216         String command = parent.eval(commandOpt, "ar crv");
8217         String fileName = parent.eval(fileNameOpt, "");
8219         bool doit = false;
8220         
8221         String fullOut = parent.resolve(fileName);
8222         //trace("ar fullout: %s", fullOut.c_str());
8223         
8224         if (!listFiles(parent, fileSet))
8225             return false;
8226         String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
8227         //trace("###########HERE %s", fileSetDir.c_str());
8229         for (unsigned int i=0 ; i<fileSet.size() ; i++)
8230             {
8231             String fname;
8232             if (fileSetDir.size()>0)
8233                 {
8234                 fname.append(fileSetDir);
8235                 fname.append("/");
8236                 }
8237             fname.append(fileSet[i]);
8238             String fullName = parent.resolve(fname);
8239             //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
8240             if (isNewerThan(fullName, fullOut))
8241                 doit = true;
8242             }
8243         //trace("Needs it:%d", doit);
8244         if (!doit)
8245             {
8246             return true;
8247             }
8249         String cmd = command;
8250         cmd.append(" ");
8251         cmd.append(fullOut);
8252         for (unsigned int i=0 ; i<fileSet.size() ; i++)
8253             {
8254             String fname;
8255             if (fileSetDir.size()>0)
8256                 {
8257                 fname.append(fileSetDir);
8258                 fname.append("/");
8259                 }
8260             fname.append(fileSet[i]);
8261             String fullName = parent.resolve(fname);
8263             cmd.append(" ");
8264             cmd.append(fullName);
8265             }
8267         String outString, errString;
8268         if (!executeCommand(cmd.c_str(), "", outString, errString))
8269             {
8270             error("<staticlib> problem: %s", errString.c_str());
8271             return false;
8272             }
8274         return true;
8275         }
8278     virtual bool parse(Element *elem)
8279         {
8280         if (!parent.getAttribute(elem, "command", commandOpt))
8281             return false;
8282         if (!parent.getAttribute(elem, "file", fileNameOpt))
8283             return false;
8284             
8285         std::vector<Element *> children = elem->getChildren();
8286         for (unsigned int i=0 ; i<children.size() ; i++)
8287             {
8288             Element *child = children[i];
8289             String tagName = child->getName();
8290             if (tagName == "fileset")
8291                 {
8292                 if (!parseFileSet(child, parent, fileSet))
8293                     return false;
8294                 }
8295             }
8296         return true;
8297         }
8299 private:
8301     FileSet fileSet;
8303     String commandOpt;
8304     String fileNameOpt;
8306 };
8311 /**
8312  * Strip an executable
8313  */
8314 class TaskStrip : public Task
8316 public:
8318     TaskStrip(MakeBase &par) : Task(par)
8319         { type = TASK_STRIP; name = "strip"; }
8321     virtual ~TaskStrip()
8322         {}
8324     virtual bool execute()
8325         {
8326         String command     = parent.eval(commandOpt, "strip");
8327         String fileName    = parent.eval(fileNameOpt, "");
8328         String symFileName = parent.eval(symFileNameOpt, "");
8330         String fullName = parent.resolve(fileName);
8331         //trace("fullDir:%s", fullDir.c_str());
8332         String cmd;
8333         String outbuf, errbuf;
8335         if (symFileName.size()>0)
8336             {
8337             String symFullName = parent.resolve(symFileName);
8338             cmd = "objcopy --only-keep-debug ";
8339             cmd.append(getNativePath(fullName));
8340             cmd.append(" ");
8341             cmd.append(getNativePath(symFullName));
8342             if (!executeCommand(cmd, "", outbuf, errbuf))
8343                 {
8344                 error("<strip> symbol file failed : %s", errbuf.c_str());
8345                 return false;
8346                 }
8347             }
8348             
8349         cmd = command;
8350         cmd.append(getNativePath(fullName));
8351         if (!executeCommand(cmd, "", outbuf, errbuf))
8352             {
8353             error("<strip> failed : %s", errbuf.c_str());
8354             return false;
8355             }
8356         return true;
8357         }
8359     virtual bool parse(Element *elem)
8360         {
8361         if (!parent.getAttribute(elem, "command", commandOpt))
8362             return false;
8363         if (!parent.getAttribute(elem, "file", fileNameOpt))
8364             return false;
8365         if (!parent.getAttribute(elem, "symfile", symFileNameOpt))
8366             return false;
8367         if (fileNameOpt.size() == 0)
8368             {
8369             error("<strip> requires 'file=\"fileName\"' attribute");
8370             return false;
8371             }
8372         return true;
8373         }
8375 private:
8377     String commandOpt;
8378     String fileNameOpt;
8379     String symFileNameOpt;
8380 };
8383 /**
8384  *
8385  */
8386 class TaskTouch : public Task
8388 public:
8390     TaskTouch(MakeBase &par) : Task(par)
8391         { type = TASK_TOUCH; name = "touch"; }
8393     virtual ~TaskTouch()
8394         {}
8396     virtual bool execute()
8397         {
8398         String fileName = parent.eval(fileNameOpt, "");
8400         String fullName = parent.resolve(fileName);
8401         String nativeFile = getNativePath(fullName);
8402         if (!isRegularFile(fullName) && !isDirectory(fullName))
8403             {            
8404             // S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH
8405             int ret = creat(nativeFile.c_str(), 0666);
8406             if (ret != 0) 
8407                 {
8408                 error("<touch> could not create '%s' : %s",
8409                     nativeFile.c_str(), strerror(ret));
8410                 return false;
8411                 }
8412             return true;
8413             }
8414         int ret = utime(nativeFile.c_str(), (struct utimbuf *)0);
8415         if (ret != 0)
8416             {
8417             error("<touch> could not update the modification time for '%s' : %s",
8418                 nativeFile.c_str(), strerror(ret));
8419             return false;
8420             }
8421         return true;
8422         }
8424     virtual bool parse(Element *elem)
8425         {
8426         //trace("touch parse");
8427         if (!parent.getAttribute(elem, "file", fileNameOpt))
8428             return false;
8429         if (fileNameOpt.size() == 0)
8430             {
8431             error("<touch> requires 'file=\"fileName\"' attribute");
8432             return false;
8433             }
8434         return true;
8435         }
8437     String fileNameOpt;
8438 };
8441 /**
8442  *
8443  */
8444 class TaskTstamp : public Task
8446 public:
8448     TaskTstamp(MakeBase &par) : Task(par)
8449         { type = TASK_TSTAMP; name = "tstamp"; }
8451     virtual ~TaskTstamp()
8452         {}
8454     virtual bool execute()
8455         {
8456         return true;
8457         }
8459     virtual bool parse(Element *elem)
8460         {
8461         //trace("tstamp parse");
8462         return true;
8463         }
8464 };
8468 /**
8469  *
8470  */
8471 Task *Task::createTask(Element *elem, int lineNr)
8473     String tagName = elem->getName();
8474     //trace("task:%s", tagName.c_str());
8475     Task *task = NULL;
8476     if (tagName == "cc")
8477         task = new TaskCC(parent);
8478     else if (tagName == "copy")
8479         task = new TaskCopy(parent);
8480     else if (tagName == "delete")
8481         task = new TaskDelete(parent);
8482     else if (tagName == "echo")
8483         task = new TaskEcho(parent);
8484     else if (tagName == "jar")
8485         task = new TaskJar(parent);
8486     else if (tagName == "javac")
8487         task = new TaskJavac(parent);
8488     else if (tagName == "link")
8489         task = new TaskLink(parent);
8490     else if (tagName == "makefile")
8491         task = new TaskMakeFile(parent);
8492     else if (tagName == "mkdir")
8493         task = new TaskMkDir(parent);
8494     else if (tagName == "msgfmt")
8495         task = new TaskMsgFmt(parent);
8496     else if (tagName == "pkg-config")
8497         task = new TaskPkgConfig(parent);
8498     else if (tagName == "ranlib")
8499         task = new TaskRanlib(parent);
8500     else if (tagName == "rc")
8501         task = new TaskRC(parent);
8502     else if (tagName == "sharedlib")
8503         task = new TaskSharedLib(parent);
8504     else if (tagName == "staticlib")
8505         task = new TaskStaticLib(parent);
8506     else if (tagName == "strip")
8507         task = new TaskStrip(parent);
8508     else if (tagName == "touch")
8509         task = new TaskTouch(parent);
8510     else if (tagName == "tstamp")
8511         task = new TaskTstamp(parent);
8512     else
8513         {
8514         error("Unknown task '%s'", tagName.c_str());
8515         return NULL;
8516         }
8518     task->setLine(lineNr);
8520     if (!task->parse(elem))
8521         {
8522         delete task;
8523         return NULL;
8524         }
8525     return task;
8530 //########################################################################
8531 //# T A R G E T
8532 //########################################################################
8534 /**
8535  *
8536  */
8537 class Target : public MakeBase
8540 public:
8542     /**
8543      *
8544      */
8545     Target(Make &par) : parent(par)
8546         { init(); }
8548     /**
8549      *
8550      */
8551     Target(const Target &other) : parent(other.parent)
8552         { init(); assign(other); }
8554     /**
8555      *
8556      */
8557     Target &operator=(const Target &other)
8558         { init(); assign(other); return *this; }
8560     /**
8561      *
8562      */
8563     virtual ~Target()
8564         { cleanup() ; }
8567     /**
8568      *
8569      */
8570     virtual Make &getParent()
8571         { return parent; }
8573     /**
8574      *
8575      */
8576     virtual String getName()
8577         { return name; }
8579     /**
8580      *
8581      */
8582     virtual void setName(const String &val)
8583         { name = val; }
8585     /**
8586      *
8587      */
8588     virtual String getDescription()
8589         { return description; }
8591     /**
8592      *
8593      */
8594     virtual void setDescription(const String &val)
8595         { description = val; }
8597     /**
8598      *
8599      */
8600     virtual void addDependency(const String &val)
8601         { deps.push_back(val); }
8603     /**
8604      *
8605      */
8606     virtual void parseDependencies(const String &val)
8607         { deps = tokenize(val, ", "); }
8609     /**
8610      *
8611      */
8612     virtual std::vector<String> &getDependencies()
8613         { return deps; }
8615     /**
8616      *
8617      */
8618     virtual String getIf()
8619         { return ifVar; }
8621     /**
8622      *
8623      */
8624     virtual void setIf(const String &val)
8625         { ifVar = val; }
8627     /**
8628      *
8629      */
8630     virtual String getUnless()
8631         { return unlessVar; }
8633     /**
8634      *
8635      */
8636     virtual void setUnless(const String &val)
8637         { unlessVar = val; }
8639     /**
8640      *
8641      */
8642     virtual void addTask(Task *val)
8643         { tasks.push_back(val); }
8645     /**
8646      *
8647      */
8648     virtual std::vector<Task *> &getTasks()
8649         { return tasks; }
8651 private:
8653     void init()
8654         {
8655         }
8657     void cleanup()
8658         {
8659         tasks.clear();
8660         }
8662     void assign(const Target &other)
8663         {
8664         //parent      = other.parent;
8665         name        = other.name;
8666         description = other.description;
8667         ifVar       = other.ifVar;
8668         unlessVar   = other.unlessVar;
8669         deps        = other.deps;
8670         tasks       = other.tasks;
8671         }
8673     Make &parent;
8675     String name;
8677     String description;
8679     String ifVar;
8681     String unlessVar;
8683     std::vector<String> deps;
8685     std::vector<Task *> tasks;
8687 };
8696 //########################################################################
8697 //# M A K E
8698 //########################################################################
8701 /**
8702  *
8703  */
8704 class Make : public MakeBase
8707 public:
8709     /**
8710      *
8711      */
8712     Make()
8713         { init(); }
8715     /**
8716      *
8717      */
8718     Make(const Make &other)
8719         { assign(other); }
8721     /**
8722      *
8723      */
8724     Make &operator=(const Make &other)
8725         { assign(other); return *this; }
8727     /**
8728      *
8729      */
8730     virtual ~Make()
8731         { cleanup(); }
8733     /**
8734      *
8735      */
8736     virtual std::map<String, Target> &getTargets()
8737         { return targets; }
8740     /**
8741      *
8742      */
8743     virtual String version()
8744         { return BUILDTOOL_VERSION; }
8746     /**
8747      * Overload a <property>
8748      */
8749     virtual bool specifyProperty(const String &name,
8750                                  const String &value);
8752     /**
8753      *
8754      */
8755     virtual bool run();
8757     /**
8758      *
8759      */
8760     virtual bool run(const String &target);
8764 private:
8766     /**
8767      *
8768      */
8769     void init();
8771     /**
8772      *
8773      */
8774     void cleanup();
8776     /**
8777      *
8778      */
8779     void assign(const Make &other);
8781     /**
8782      *
8783      */
8784     bool executeTask(Task &task);
8787     /**
8788      *
8789      */
8790     bool executeTarget(Target &target,
8791              std::set<String> &targetsCompleted);
8794     /**
8795      *
8796      */
8797     bool execute();
8799     /**
8800      *
8801      */
8802     bool checkTargetDependencies(Target &prop,
8803                     std::vector<String> &depList);
8805     /**
8806      *
8807      */
8808     bool parsePropertyFile(const String &fileName,
8809                            const String &prefix);
8811     /**
8812      *
8813      */
8814     bool parseProperty(Element *elem);
8816     /**
8817      *
8818      */
8819     bool parseFile();
8821     /**
8822      *
8823      */
8824     std::vector<String> glob(const String &pattern);
8827     //###############
8828     //# Fields
8829     //###############
8831     String projectName;
8833     String currentTarget;
8835     String defaultTarget;
8837     String specifiedTarget;
8839     String baseDir;
8841     String description;
8842     
8843     //std::vector<Property> properties;
8844     
8845     std::map<String, Target> targets;
8847     std::vector<Task *> allTasks;
8848     
8849     std::map<String, String> specifiedProperties;
8851 };
8854 //########################################################################
8855 //# C L A S S  M A I N T E N A N C E
8856 //########################################################################
8858 /**
8859  *
8860  */
8861 void Make::init()
8863     uri             = "build.xml";
8864     projectName     = "";
8865     currentTarget   = "";
8866     defaultTarget   = "";
8867     specifiedTarget = "";
8868     baseDir         = "";
8869     description     = "";
8870     envPrefix       = "env.";
8871     pcPrefix        = "pc.";
8872     pccPrefix       = "pcc.";
8873     pclPrefix       = "pcl.";
8874     properties.clear();
8875     for (unsigned int i = 0 ; i < allTasks.size() ; i++)
8876         delete allTasks[i];
8877     allTasks.clear();
8882 /**
8883  *
8884  */
8885 void Make::cleanup()
8887     for (unsigned int i = 0 ; i < allTasks.size() ; i++)
8888         delete allTasks[i];
8889     allTasks.clear();
8894 /**
8895  *
8896  */
8897 void Make::assign(const Make &other)
8899     uri              = other.uri;
8900     projectName      = other.projectName;
8901     currentTarget    = other.currentTarget;
8902     defaultTarget    = other.defaultTarget;
8903     specifiedTarget  = other.specifiedTarget;
8904     baseDir          = other.baseDir;
8905     description      = other.description;
8906     properties       = other.properties;
8911 //########################################################################
8912 //# U T I L I T Y    T A S K S
8913 //########################################################################
8915 /**
8916  *  Perform a file globbing
8917  */
8918 std::vector<String> Make::glob(const String &pattern)
8920     std::vector<String> res;
8921     return res;
8925 //########################################################################
8926 //# P U B L I C    A P I
8927 //########################################################################
8931 /**
8932  *
8933  */
8934 bool Make::executeTarget(Target &target,
8935              std::set<String> &targetsCompleted)
8938     String name = target.getName();
8940     //First get any dependencies for this target
8941     std::vector<String> deps = target.getDependencies();
8942     for (unsigned int i=0 ; i<deps.size() ; i++)
8943         {
8944         String dep = deps[i];
8945         //Did we do it already?  Skip
8946         if (targetsCompleted.find(dep)!=targetsCompleted.end())
8947             continue;
8948             
8949         std::map<String, Target> &tgts =
8950                target.getParent().getTargets();
8951         std::map<String, Target>::iterator iter =
8952                tgts.find(dep);
8953         if (iter == tgts.end())
8954             {
8955             error("Target '%s' dependency '%s' not found",
8956                       name.c_str(),  dep.c_str());
8957             return false;
8958             }
8959         Target depTarget = iter->second;
8960         if (!executeTarget(depTarget, targetsCompleted))
8961             {
8962             return false;
8963             }
8964         }
8966     status("##### Target : %s\n##### %s", name.c_str(),
8967             target.getDescription().c_str());
8969     //Now let's do the tasks
8970     std::vector<Task *> &tasks = target.getTasks();
8971     for (unsigned int i=0 ; i<tasks.size() ; i++)
8972         {
8973         Task *task = tasks[i];
8974         status("--- %s / %s", name.c_str(), task->getName().c_str());
8975         if (!task->execute())
8976             {
8977             return false;
8978             }
8979         }
8980         
8981     targetsCompleted.insert(name);
8982     
8983     return true;
8988 /**
8989  *  Main execute() method.  Start here and work
8990  *  up the dependency tree 
8991  */
8992 bool Make::execute()
8994     status("######## EXECUTE");
8996     //Determine initial target
8997     if (specifiedTarget.size()>0)
8998         {
8999         currentTarget = specifiedTarget;
9000         }
9001     else if (defaultTarget.size()>0)
9002         {
9003         currentTarget = defaultTarget;
9004         }
9005     else
9006         {
9007         error("execute: no specified or default target requested");
9008         return false;
9009         }
9011     std::map<String, Target>::iterator iter =
9012                targets.find(currentTarget);
9013     if (iter == targets.end())
9014         {
9015         error("Initial target '%s' not found",
9016                  currentTarget.c_str());
9017         return false;
9018         }
9019         
9020     //Now run
9021     Target target = iter->second;
9022     std::set<String> targetsCompleted;
9023     if (!executeTarget(target, targetsCompleted))
9024         {
9025         return false;
9026         }
9028     status("######## EXECUTE COMPLETE");
9029     return true;
9035 /**
9036  *
9037  */
9038 bool Make::checkTargetDependencies(Target &target, 
9039                             std::vector<String> &depList)
9041     String tgtName = target.getName().c_str();
9042     depList.push_back(tgtName);
9044     std::vector<String> deps = target.getDependencies();
9045     for (unsigned int i=0 ; i<deps.size() ; i++)
9046         {
9047         String dep = deps[i];
9048         //First thing entered was the starting Target
9049         if (dep == depList[0])
9050             {
9051             error("Circular dependency '%s' found at '%s'",
9052                       dep.c_str(), tgtName.c_str());
9053             std::vector<String>::iterator diter;
9054             for (diter=depList.begin() ; diter!=depList.end() ; diter++)
9055                 {
9056                 error("  %s", diter->c_str());
9057                 }
9058             return false;
9059             }
9061         std::map<String, Target> &tgts =
9062                   target.getParent().getTargets();
9063         std::map<String, Target>::iterator titer = tgts.find(dep);
9064         if (titer == tgts.end())
9065             {
9066             error("Target '%s' dependency '%s' not found",
9067                       tgtName.c_str(), dep.c_str());
9068             return false;
9069             }
9070         if (!checkTargetDependencies(titer->second, depList))
9071             {
9072             return false;
9073             }
9074         }
9075     return true;
9082 static int getword(int pos, const String &inbuf, String &result)
9084     int p = pos;
9085     int len = (int)inbuf.size();
9086     String val;
9087     while (p < len)
9088         {
9089         char ch = inbuf[p];
9090         if (!isalnum(ch) && ch!='.' && ch!='_')
9091             break;
9092         val.push_back(ch);
9093         p++;
9094         }
9095     result = val;
9096     return p;
9102 /**
9103  *
9104  */
9105 bool Make::parsePropertyFile(const String &fileName,
9106                              const String &prefix)
9108     FILE *f = fopen(fileName.c_str(), "r");
9109     if (!f)
9110         {
9111         error("could not open property file %s", fileName.c_str());
9112         return false;
9113         }
9114     int linenr = 0;
9115     while (!feof(f))
9116         {
9117         char buf[256];
9118         if (!fgets(buf, 255, f))
9119             break;
9120         linenr++;
9121         String s = buf;
9122         s = trim(s);
9123         int len = s.size();
9124         if (len == 0)
9125             continue;
9126         if (s[0] == '#')
9127             continue;
9128         String key;
9129         String val;
9130         int p = 0;
9131         int p2 = getword(p, s, key);
9132         if (p2 <= p)
9133             {
9134             error("property file %s, line %d: expected keyword",
9135                     fileName.c_str(), linenr);
9136             return false;
9137             }
9138         if (prefix.size() > 0)
9139             {
9140             key.insert(0, prefix);
9141             }
9143         //skip whitespace
9144         for (p=p2 ; p<len ; p++)
9145             if (!isspace(s[p]))
9146                 break;
9148         if (p>=len || s[p]!='=')
9149             {
9150             error("property file %s, line %d: expected '='",
9151                     fileName.c_str(), linenr);
9152             return false;
9153             }
9154         p++;
9156         //skip whitespace
9157         for ( ; p<len ; p++)
9158             if (!isspace(s[p]))
9159                 break;
9161         /* This way expects a word after the =
9162         p2 = getword(p, s, val);
9163         if (p2 <= p)
9164             {
9165             error("property file %s, line %d: expected value",
9166                     fileName.c_str(), linenr);
9167             return false;
9168             }
9169         */
9170         // This way gets the rest of the line after the =
9171         if (p>=len)
9172             {
9173             error("property file %s, line %d: expected value",
9174                     fileName.c_str(), linenr);
9175             return false;
9176             }
9177         val = s.substr(p);
9178         if (key.size()==0)
9179             continue;
9180         //allow property to be set, even if val=""
9182         //trace("key:'%s' val:'%s'", key.c_str(), val.c_str());
9183         //See if we wanted to overload this property
9184         std::map<String, String>::iterator iter =
9185             specifiedProperties.find(key);
9186         if (iter!=specifiedProperties.end())
9187             {
9188             val = iter->second;
9189             status("overloading property '%s' = '%s'",
9190                    key.c_str(), val.c_str());
9191             }
9192         properties[key] = val;
9193         }
9194     fclose(f);
9195     return true;
9201 /**
9202  *
9203  */
9204 bool Make::parseProperty(Element *elem)
9206     std::vector<Attribute> &attrs = elem->getAttributes();
9207     for (unsigned int i=0 ; i<attrs.size() ; i++)
9208         {
9209         String attrName = attrs[i].getName();
9210         String attrVal  = attrs[i].getValue();
9212         if (attrName == "name")
9213             {
9214             String val;
9215             if (!getAttribute(elem, "value", val))
9216                 return false;
9217             if (val.size() > 0)
9218                 {
9219                 properties[attrVal] = val;
9220                 }
9221             else
9222                 {
9223                 if (!getAttribute(elem, "location", val))
9224                     return false;
9225                 //let the property exist, even if not defined
9226                 properties[attrVal] = val;
9227                 }
9228             //See if we wanted to overload this property
9229             std::map<String, String>::iterator iter =
9230                 specifiedProperties.find(attrVal);
9231             if (iter != specifiedProperties.end())
9232                 {
9233                 val = iter->second;
9234                 status("overloading property '%s' = '%s'",
9235                     attrVal.c_str(), val.c_str());
9236                 properties[attrVal] = val;
9237                 }
9238             }
9239         else if (attrName == "file")
9240             {
9241             String prefix;
9242             if (!getAttribute(elem, "prefix", prefix))
9243                 return false;
9244             if (prefix.size() > 0)
9245                 {
9246                 if (prefix[prefix.size()-1] != '.')
9247                     prefix.push_back('.');
9248                 }
9249             if (!parsePropertyFile(attrName, prefix))
9250                 return false;
9251             }
9252         else if (attrName == "environment")
9253             {
9254             if (attrVal.find('.') != attrVal.npos)
9255                 {
9256                 error("environment prefix cannot have a '.' in it");
9257                 return false;
9258                 }
9259             envPrefix = attrVal;
9260             envPrefix.push_back('.');
9261             }
9262         else if (attrName == "pkg-config")
9263             {
9264             if (attrVal.find('.') != attrVal.npos)
9265                 {
9266                 error("pkg-config prefix cannot have a '.' in it");
9267                 return false;
9268                 }
9269             pcPrefix = attrVal;
9270             pcPrefix.push_back('.');
9271             }
9272         else if (attrName == "pkg-config-cflags")
9273             {
9274             if (attrVal.find('.') != attrVal.npos)
9275                 {
9276                 error("pkg-config-cflags prefix cannot have a '.' in it");
9277                 return false;
9278                 }
9279             pccPrefix = attrVal;
9280             pccPrefix.push_back('.');
9281             }
9282         else if (attrName == "pkg-config-libs")
9283             {
9284             if (attrVal.find('.') != attrVal.npos)
9285                 {
9286                 error("pkg-config-libs prefix cannot have a '.' in it");
9287                 return false;
9288                 }
9289             pclPrefix = attrVal;
9290             pclPrefix.push_back('.');
9291             }
9292         }
9294     return true;
9300 /**
9301  *
9302  */
9303 bool Make::parseFile()
9305     status("######## PARSE : %s", uri.getPath().c_str());
9307     setLine(0);
9309     Parser parser;
9310     Element *root = parser.parseFile(uri.getNativePath());
9311     if (!root)
9312         {
9313         error("Could not open %s for reading",
9314               uri.getNativePath().c_str());
9315         return false;
9316         }
9317     
9318     setLine(root->getLine());
9320     if (root->getChildren().size()==0 ||
9321         root->getChildren()[0]->getName()!="project")
9322         {
9323         error("Main xml element should be <project>");
9324         delete root;
9325         return false;
9326         }
9328     //########## Project attributes
9329     Element *project = root->getChildren()[0];
9330     String s = project->getAttribute("name");
9331     if (s.size() > 0)
9332         projectName = s;
9333     s = project->getAttribute("default");
9334     if (s.size() > 0)
9335         defaultTarget = s;
9336     s = project->getAttribute("basedir");
9337     if (s.size() > 0)
9338         baseDir = s;
9340     //######### PARSE MEMBERS
9341     std::vector<Element *> children = project->getChildren();
9342     for (unsigned int i=0 ; i<children.size() ; i++)
9343         {
9344         Element *elem = children[i];
9345         setLine(elem->getLine());
9346         String tagName = elem->getName();
9348         //########## DESCRIPTION
9349         if (tagName == "description")
9350             {
9351             description = parser.trim(elem->getValue());
9352             }
9354         //######### PROPERTY
9355         else if (tagName == "property")
9356             {
9357             if (!parseProperty(elem))
9358                 return false;
9359             }
9361         //######### TARGET
9362         else if (tagName == "target")
9363             {
9364             String tname   = elem->getAttribute("name");
9365             String tdesc   = elem->getAttribute("description");
9366             String tdeps   = elem->getAttribute("depends");
9367             String tif     = elem->getAttribute("if");
9368             String tunless = elem->getAttribute("unless");
9369             Target target(*this);
9370             target.setName(tname);
9371             target.setDescription(tdesc);
9372             target.parseDependencies(tdeps);
9373             target.setIf(tif);
9374             target.setUnless(tunless);
9375             std::vector<Element *> telems = elem->getChildren();
9376             for (unsigned int i=0 ; i<telems.size() ; i++)
9377                 {
9378                 Element *telem = telems[i];
9379                 Task breeder(*this);
9380                 Task *task = breeder.createTask(telem, telem->getLine());
9381                 if (!task)
9382                     return false;
9383                 allTasks.push_back(task);
9384                 target.addTask(task);
9385                 }
9387             //Check name
9388             if (tname.size() == 0)
9389                 {
9390                 error("no name for target");
9391                 return false;
9392                 }
9393             //Check for duplicate name
9394             if (targets.find(tname) != targets.end())
9395                 {
9396                 error("target '%s' already defined", tname.c_str());
9397                 return false;
9398                 }
9399             //more work than targets[tname]=target, but avoids default allocator
9400             targets.insert(std::make_pair<String, Target>(tname, target));
9401             }
9402         //######### none of the above
9403         else
9404             {
9405             error("unknown toplevel tag: <%s>", tagName.c_str());
9406             return false;
9407             }
9409         }
9411     std::map<String, Target>::iterator iter;
9412     for (iter = targets.begin() ; iter!= targets.end() ; iter++)
9413         {
9414         Target tgt = iter->second;
9415         std::vector<String> depList;
9416         if (!checkTargetDependencies(tgt, depList))
9417             {
9418             return false;
9419             }
9420         }
9423     delete root;
9424     status("######## PARSE COMPLETE");
9425     return true;
9429 /**
9430  * Overload a <property>
9431  */
9432 bool Make::specifyProperty(const String &name, const String &value)
9434     if (specifiedProperties.find(name) != specifiedProperties.end())
9435         {
9436         error("Property %s already specified", name.c_str());
9437         return false;
9438         }
9439     specifiedProperties[name] = value;
9440     return true;
9445 /**
9446  *
9447  */
9448 bool Make::run()
9450     if (!parseFile())
9451         return false;
9452         
9453     if (!execute())
9454         return false;
9456     return true;
9462 /**
9463  * Get a formatted MM:SS.sss time elapsed string
9464  */ 
9465 static String
9466 timeDiffString(struct timeval &x, struct timeval &y)
9468     long microsX  = x.tv_usec;
9469     long secondsX = x.tv_sec;
9470     long microsY  = y.tv_usec;
9471     long secondsY = y.tv_sec;
9472     if (microsX < microsY)
9473         {
9474         microsX += 1000000;
9475         secondsX -= 1;
9476         }
9478     int seconds = (int)(secondsX - secondsY);
9479     int millis  = (int)((microsX - microsY)/1000);
9481     int minutes = seconds/60;
9482     seconds -= minutes*60;
9483     char buf[80];
9484     snprintf(buf, 79, "%dm %d.%03ds", minutes, seconds, millis);
9485     String ret = buf;
9486     return ret;
9487     
9490 /**
9491  *
9492  */
9493 bool Make::run(const String &target)
9495     status("####################################################");
9496     status("#   %s", version().c_str());
9497     status("####################################################");
9498     struct timeval timeStart, timeEnd;
9499     ::gettimeofday(&timeStart, NULL);
9500     specifiedTarget = target;
9501     if (!run())
9502         return false;
9503     ::gettimeofday(&timeEnd, NULL);
9504     String timeStr = timeDiffString(timeEnd, timeStart);
9505     status("####################################################");
9506     status("#   BuildTool Completed : %s", timeStr.c_str());
9507     status("####################################################");
9508     return true;
9517 }// namespace buildtool
9518 //########################################################################
9519 //# M A I N
9520 //########################################################################
9522 typedef buildtool::String String;
9524 /**
9525  *  Format an error message in printf() style
9526  */
9527 static void error(const char *fmt, ...)
9529     va_list ap;
9530     va_start(ap, fmt);
9531     fprintf(stderr, "BuildTool error: ");
9532     vfprintf(stderr, fmt, ap);
9533     fprintf(stderr, "\n");
9534     va_end(ap);
9538 static bool parseProperty(const String &s, String &name, String &val)
9540     int len = s.size();
9541     int i;
9542     for (i=0 ; i<len ; i++)
9543         {
9544         char ch = s[i];
9545         if (ch == '=')
9546             break;
9547         name.push_back(ch);
9548         }
9549     if (i>=len || s[i]!='=')
9550         {
9551         error("property requires -Dname=value");
9552         return false;
9553         }
9554     i++;
9555     for ( ; i<len ; i++)
9556         {
9557         char ch = s[i];
9558         val.push_back(ch);
9559         }
9560     return true;
9564 /**
9565  * Compare a buffer with a key, for the length of the key
9566  */
9567 static bool sequ(const String &buf, const char *key)
9569     int len = buf.size();
9570     for (int i=0 ; key[i] && i<len ; i++)
9571         {
9572         if (key[i] != buf[i])
9573             return false;
9574         }        
9575     return true;
9578 static void usage(int argc, char **argv)
9580     printf("usage:\n");
9581     printf("   %s [options] [target]\n", argv[0]);
9582     printf("Options:\n");
9583     printf("  -help, -h              print this message\n");
9584     printf("  -version               print the version information and exit\n");
9585     printf("  -file <file>           use given buildfile\n");
9586     printf("  -f <file>                 ''\n");
9587     printf("  -D<property>=<value>   use value for given property\n");
9593 /**
9594  * Parse the command-line args, get our options,
9595  * and run this thing
9596  */   
9597 static bool parseOptions(int argc, char **argv)
9599     if (argc < 1)
9600         {
9601         error("Cannot parse arguments");
9602         return false;
9603         }
9605     buildtool::Make make;
9607     String target;
9609     //char *progName = argv[0];
9610     for (int i=1 ; i<argc ; i++)
9611         {
9612         String arg = argv[i];
9613         if (arg.size()>1 && arg[0]=='-')
9614             {
9615             if (arg == "-h" || arg == "-help")
9616                 {
9617                 usage(argc,argv);
9618                 return true;
9619                 }
9620             else if (arg == "-version")
9621                 {
9622                 printf("%s", make.version().c_str());
9623                 return true;
9624                 }
9625             else if (arg == "-f" || arg == "-file")
9626                 {
9627                 if (i>=argc)
9628                    {
9629                    usage(argc, argv);
9630                    return false;
9631                    }
9632                 i++; //eat option
9633                 make.setURI(argv[i]);
9634                 }
9635             else if (arg.size()>2 && sequ(arg, "-D"))
9636                 {
9637                 String s = arg.substr(2, arg.size());
9638                 String name, value;
9639                 if (!parseProperty(s, name, value))
9640                    {
9641                    usage(argc, argv);
9642                    return false;
9643                    }
9644                 if (!make.specifyProperty(name, value))
9645                     return false;
9646                 }
9647             else
9648                 {
9649                 error("Unknown option:%s", arg.c_str());
9650                 return false;
9651                 }
9652             }
9653         else
9654             {
9655             if (target.size()>0)
9656                 {
9657                 error("only one initial target");
9658                 usage(argc, argv);
9659                 return false;
9660                 }
9661             target = arg;
9662             }
9663         }
9665     //We have the options.  Now execute them
9666     if (!make.run(target))
9667         return false;
9669     return true;
9675 /*
9676 static bool runMake()
9678     buildtool::Make make;
9679     if (!make.run())
9680         return false;
9681     return true;
9685 static bool pkgConfigTest()
9687     buildtool::PkgConfig pkgConfig;
9688     if (!pkgConfig.readFile("gtk+-2.0.pc"))
9689         return false;
9690     return true;
9695 static bool depTest()
9697     buildtool::DepTool deptool;
9698     deptool.setSourceDirectory("/dev/ink/inkscape/src");
9699     if (!deptool.generateDependencies("build.dep"))
9700         return false;
9701     std::vector<buildtool::FileRec> res =
9702            deptool.loadDepFile("build.dep");
9703     if (res.size() == 0)
9704         return false;
9705     return true;
9708 static bool popenTest()
9710     buildtool::Make make;
9711     buildtool::String out, err;
9712     bool ret = make.executeCommand("gcc xx.cpp", "", out, err);
9713     printf("Popen test:%d '%s' '%s'\n", ret, out.c_str(), err.c_str());
9714     return true;
9718 static bool propFileTest()
9720     buildtool::Make make;
9721     make.parsePropertyFile("test.prop", "test.");
9722     return true;
9724 */
9726 int main(int argc, char **argv)
9729     if (!parseOptions(argc, argv))
9730         return 1;
9731     /*
9732     if (!popenTest())
9733         return 1;
9735     if (!depTest())
9736         return 1;
9737     if (!propFileTest())
9738         return 1;
9739     if (runMake())
9740         return 1;
9741     */
9742     return 0;
9746 //########################################################################
9747 //# E N D 
9748 //########################################################################