Code

finally debugged. read() on a pipe has different results on different boxes. sorry...
[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 || ch <= 0)
4148                 outOpen = false;
4149             else
4150                 outb.push_back(ch);
4151             }
4152         if (FD_ISSET(errRead, &fdset))
4153             {
4154             if (read(errRead, &ch, 1) <= 0 || ch <= 0)
4155                 errOpen = false;
4156             else
4157                 errb.push_back(ch);
4158             }
4159         }
4161     int childReturnValue;
4162     wait(&childReturnValue);
4164     close(outRead);
4165     close(errRead);
4167     outbuf = outb;
4168     errbuf = errb;
4170     if (childReturnValue != 0)
4171         {
4172         error("exec of command '%s' failed : %s",
4173              command.c_str(), strerror(childReturnValue));
4174         return false;
4175         }
4177     return true;
4178
4180 #endif
4185 bool MakeBase::listDirectories(const String &baseName,
4186                               const String &dirName,
4187                               std::vector<String> &res)
4189     res.push_back(dirName);
4190     String fullPath = baseName;
4191     if (dirName.size()>0)
4192         {
4193         fullPath.append("/");
4194         fullPath.append(dirName);
4195         }
4196     DIR *dir = opendir(fullPath.c_str());
4197     while (true)
4198         {
4199         struct dirent *de = readdir(dir);
4200         if (!de)
4201             break;
4203         //Get the directory member name
4204         String s = de->d_name;
4205         if (s.size() == 0 || s[0] == '.')
4206             continue;
4207         String childName = dirName;
4208         childName.append("/");
4209         childName.append(s);
4211         String fullChildPath = baseName;
4212         fullChildPath.append("/");
4213         fullChildPath.append(childName);
4214         struct stat finfo;
4215         String childNative = getNativePath(fullChildPath);
4216         if (stat(childNative.c_str(), &finfo)<0)
4217             {
4218             error("cannot stat file:%s", childNative.c_str());
4219             }
4220         else if (S_ISDIR(finfo.st_mode))
4221             {
4222             //trace("directory: %s", childName.c_str());
4223             if (!listDirectories(baseName, childName, res))
4224                 return false;
4225             }
4226         }
4227     closedir(dir);
4229     return true;
4233 bool MakeBase::listFiles(const String &baseDir,
4234                          const String &dirName,
4235                          std::vector<String> &res)
4237     String fullDir = baseDir;
4238     if (dirName.size()>0)
4239         {
4240         fullDir.append("/");
4241         fullDir.append(dirName);
4242         }
4243     String dirNative = getNativePath(fullDir);
4245     std::vector<String> subdirs;
4246     DIR *dir = opendir(dirNative.c_str());
4247     if (!dir)
4248         {
4249         error("Could not open directory %s : %s",
4250               dirNative.c_str(), strerror(errno));
4251         return false;
4252         }
4253     while (true)
4254         {
4255         struct dirent *de = readdir(dir);
4256         if (!de)
4257             break;
4259         //Get the directory member name
4260         String s = de->d_name;
4261         if (s.size() == 0 || s[0] == '.')
4262             continue;
4263         String childName;
4264         if (dirName.size()>0)
4265             {
4266             childName.append(dirName);
4267             childName.append("/");
4268             }
4269         childName.append(s);
4270         String fullChild = baseDir;
4271         fullChild.append("/");
4272         fullChild.append(childName);
4273         
4274         if (isDirectory(fullChild))
4275             {
4276             //trace("directory: %s", childName.c_str());
4277             if (!listFiles(baseDir, childName, res))
4278                 return false;
4279             continue;
4280             }
4281         else if (!isRegularFile(fullChild))
4282             {
4283             error("unknown file:%s", childName.c_str());
4284             return false;
4285             }
4287        //all done!
4288         res.push_back(childName);
4290         }
4291     closedir(dir);
4293     return true;
4297 /**
4298  * Several different classes extend MakeBase.  By "propRef", we mean
4299  * the one holding the properties.  Likely "Make" itself
4300  */
4301 bool MakeBase::listFiles(MakeBase &propRef, FileSet &fileSet)
4303     //before doing the list,  resolve any property references
4304     //that might have been specified in the directory name, such as ${src}
4305     String fsDir = fileSet.getDirectory();
4306     String dir;
4307     if (!propRef.getSubstitutions(fsDir, dir))
4308         return false;
4309     String baseDir = propRef.resolve(dir);
4310     std::vector<String> fileList;
4311     if (!listFiles(baseDir, "", fileList))
4312         return false;
4314     std::vector<String> includes = fileSet.getIncludes();
4315     std::vector<String> excludes = fileSet.getExcludes();
4317     std::vector<String> incs;
4318     std::vector<String>::iterator iter;
4320     std::sort(fileList.begin(), fileList.end());
4322     //If there are <includes>, then add files to the output
4323     //in the order of the include list
4324     if (includes.size()==0)
4325         incs = fileList;
4326     else
4327         {
4328         for (iter = includes.begin() ; iter != includes.end() ; iter++)
4329             {
4330             String &pattern = *iter;
4331             std::vector<String>::iterator siter;
4332             for (siter = fileList.begin() ; siter != fileList.end() ; siter++)
4333                 {
4334                 String s = *siter;
4335                 if (regexMatch(s, pattern))
4336                     {
4337                     //trace("INCLUDED:%s", s.c_str());
4338                     incs.push_back(s);
4339                     }
4340                 }
4341             }
4342         }
4344     //Now trim off the <excludes>
4345     std::vector<String> res;
4346     for (iter = incs.begin() ; iter != incs.end() ; iter++)
4347         {
4348         String s = *iter;
4349         bool skipme = false;
4350         std::vector<String>::iterator siter;
4351         for (siter = excludes.begin() ; siter != excludes.end() ; siter++)
4352             {
4353             String &pattern = *siter;
4354             if (regexMatch(s, pattern))
4355                 {
4356                 //trace("EXCLUDED:%s", s.c_str());
4357                 skipme = true;
4358                 break;
4359                 }
4360             }
4361         if (!skipme)
4362             res.push_back(s);
4363         }
4364         
4365     fileSet.setFiles(res);
4367     return true;
4371 /**
4372  * 0 == all, 1 = cflags, 2 = libs
4373  */ 
4374 bool MakeBase::pkgConfigRecursive(const String packageName,
4375                                   const String &path, 
4376                                   const String &prefix, 
4377                                   int query,
4378                                   String &result,
4379                                   std::set<String> &deplist) 
4381     PkgConfig pkgConfig;
4382     if (path.size() > 0)
4383         pkgConfig.setPath(path);
4384     if (prefix.size() > 0)
4385         pkgConfig.setPrefix(prefix);
4386     if (!pkgConfig.query(packageName))
4387         return false;
4388     if (query == 0)
4389         result = pkgConfig.getAll();
4390     else if (query == 1)
4391         result = pkgConfig.getCflags();
4392     else
4393         result = pkgConfig.getLibs();
4394     deplist.insert(packageName);
4395     std::vector<String> list = pkgConfig.getRequireList();
4396     for (unsigned int i = 0 ; i<list.size() ; i++)
4397         {
4398         String depPkgName = list[i];
4399         if (deplist.find(depPkgName) != deplist.end())
4400             continue;
4401         String val;
4402         if (!pkgConfigRecursive(depPkgName, path, prefix, query, val, deplist))
4403             {
4404             error("Based on 'requires' attribute of package '%s'", packageName.c_str());
4405             return false;
4406             }
4407         result.append(" ");
4408         result.append(val);
4409         }
4411     return true;
4414 bool MakeBase::pkgConfigQuery(const String &packageName, int query, String &result)
4416     std::set<String> deplist;
4417     String path = getProperty("pkg-config-path");
4418     if (path.size()>0)
4419         path = resolve(path);
4420     String prefix = getProperty("pkg-config-prefix");
4421     String val;
4422     if (!pkgConfigRecursive(packageName, path, prefix, query, val, deplist))
4423         return false;
4424     result = val;
4425     return true;
4430 /**
4431  * replace a variable ref like ${a} with a value
4432  */
4433 bool MakeBase::lookupProperty(const String &propertyName, String &result)
4435     String varname = propertyName;
4436     if (envPrefix.size() > 0 &&
4437         varname.compare(0, envPrefix.size(), envPrefix) == 0)
4438         {
4439         varname = varname.substr(envPrefix.size());
4440         char *envstr = getenv(varname.c_str());
4441         if (!envstr)
4442             {
4443             error("environment variable '%s' not defined", varname.c_str());
4444             return false;
4445             }
4446         result = envstr;
4447         }
4448     else if (pcPrefix.size() > 0 &&
4449         varname.compare(0, pcPrefix.size(), pcPrefix) == 0)
4450         {
4451         varname = varname.substr(pcPrefix.size());
4452         String val;
4453         if (!pkgConfigQuery(varname, 0, val))
4454             return false;
4455         result = val;
4456         }
4457     else if (pccPrefix.size() > 0 &&
4458         varname.compare(0, pccPrefix.size(), pccPrefix) == 0)
4459         {
4460         varname = varname.substr(pccPrefix.size());
4461         String val;
4462         if (!pkgConfigQuery(varname, 1, val))
4463             return false;
4464         result = val;
4465         }
4466     else if (pclPrefix.size() > 0 &&
4467         varname.compare(0, pclPrefix.size(), pclPrefix) == 0)
4468         {
4469         varname = varname.substr(pclPrefix.size());
4470         String val;
4471         if (!pkgConfigQuery(varname, 2, val))
4472             return false;
4473         result = val;
4474         }
4475     else
4476         {
4477         std::map<String, String>::iterator iter;
4478         iter = properties.find(varname);
4479         if (iter != properties.end())
4480             {
4481             result = iter->second;
4482             }
4483         else
4484             {
4485             error("property '%s' not found", varname.c_str());
4486             return false;
4487             }
4488         }
4489     return true;
4495 /**
4496  * Analyse a string, looking for any substitutions or other
4497  * things that need resolution 
4498  */
4499 bool MakeBase::getSubstitutionsRecursive(const String &str,
4500                                          String &result, int depth)
4502     if (depth > 10)
4503         {
4504         error("nesting of substitutions too deep (>10) for '%s'",
4505                         str.c_str());
4506         return false;
4507         }
4508     String s = trim(str);
4509     int len = (int)s.size();
4510     String val;
4511     for (int i=0 ; i<len ; i++)
4512         {
4513         char ch = s[i];
4514         if (ch == '$' && s[i+1] == '{')
4515             {
4516             String varname;
4517             int j = i+2;
4518             for ( ; j<len ; j++)
4519                 {
4520                 ch = s[j];
4521                 if (ch == '$' && s[j+1] == '{')
4522                     {
4523                     error("attribute %s cannot have nested variable references",
4524                            s.c_str());
4525                     return false;
4526                     }
4527                 else if (ch == '}')
4528                     {
4529                     varname = trim(varname);
4530                     String varval;
4531                     if (!lookupProperty(varname, varval))
4532                         return false;
4533                     String varval2;
4534                     //Now see if the answer has ${} in it, too
4535                     if (!getSubstitutionsRecursive(varval, varval2, depth + 1))
4536                         return false;
4537                     val.append(varval2);
4538                     break;
4539                     }
4540                 else
4541                     {
4542                     varname.push_back(ch);
4543                     }
4544                 }
4545             i = j;
4546             }
4547         else
4548             {
4549             val.push_back(ch);
4550             }
4551         }
4552     result = val;
4553     return true;
4556 /**
4557  * Analyse a string, looking for any substitutions or other
4558  * things that need resilution 
4559  */
4560 bool MakeBase::getSubstitutions(const String &str, String &result)
4562     return getSubstitutionsRecursive(str, result, 0);
4567 /**
4568  * replace variable refs like ${a} with their values
4569  * Assume that the string has already been syntax validated
4570  */
4571 String MakeBase::eval(const String &s, const String &defaultVal)
4573     if (s.size()==0)
4574         return defaultVal;
4575     String ret;
4576     if (getSubstitutions(s, ret))
4577         return ret;
4578     else
4579         return defaultVal;
4583 /**
4584  * replace variable refs like ${a} with their values
4585  * return true or false
4586  * Assume that the string has already been syntax validated
4587  */
4588 bool MakeBase::evalBool(const String &s, bool defaultVal)
4590     if (s.size()==0)
4591         return defaultVal;
4592     String val = eval(s, "false");
4593     if (val.size()==0)
4594         return defaultVal;
4595     if (val == "true" || val == "TRUE")
4596         return true;
4597     else
4598         return false;
4602 /**
4603  * Get a string attribute, testing it for proper syntax and
4604  * property names.
4605  */
4606 bool MakeBase::getAttribute(Element *elem, const String &name,
4607                                     String &result)
4609     String s = elem->getAttribute(name);
4610     String tmp;
4611     bool ret = getSubstitutions(s, tmp);
4612     if (ret)
4613         result = s;  //assign -if- ok
4614     return ret;
4618 /**
4619  * Get a string value, testing it for proper syntax and
4620  * property names.
4621  */
4622 bool MakeBase::getValue(Element *elem, String &result)
4624     String s = elem->getValue();
4625     String tmp;
4626     bool ret = getSubstitutions(s, tmp);
4627     if (ret)
4628         result = s;  //assign -if- ok
4629     return ret;
4635 /**
4636  * Parse a <patternset> entry
4637  */  
4638 bool MakeBase::parsePatternSet(Element *elem,
4639                           MakeBase &propRef,
4640                           std::vector<String> &includes,
4641                           std::vector<String> &excludes
4642                           )
4644     std::vector<Element *> children  = elem->getChildren();
4645     for (unsigned int i=0 ; i<children.size() ; i++)
4646         {
4647         Element *child = children[i];
4648         String tagName = child->getName();
4649         if (tagName == "exclude")
4650             {
4651             String fname;
4652             if (!propRef.getAttribute(child, "name", fname))
4653                 return false;
4654             //trace("EXCLUDE: %s", fname.c_str());
4655             excludes.push_back(fname);
4656             }
4657         else if (tagName == "include")
4658             {
4659             String fname;
4660             if (!propRef.getAttribute(child, "name", fname))
4661                 return false;
4662             //trace("INCLUDE: %s", fname.c_str());
4663             includes.push_back(fname);
4664             }
4665         }
4667     return true;
4673 /**
4674  * Parse a <fileset> entry, and determine which files
4675  * should be included
4676  */  
4677 bool MakeBase::parseFileSet(Element *elem,
4678                           MakeBase &propRef,
4679                           FileSet &fileSet)
4681     String name = elem->getName();
4682     if (name != "fileset")
4683         {
4684         error("expected <fileset>");
4685         return false;
4686         }
4689     std::vector<String> includes;
4690     std::vector<String> excludes;
4692     //A fileset has one implied patternset
4693     if (!parsePatternSet(elem, propRef, includes, excludes))
4694         {
4695         return false;
4696         }
4697     //Look for child tags, including more patternsets
4698     std::vector<Element *> children  = elem->getChildren();
4699     for (unsigned int i=0 ; i<children.size() ; i++)
4700         {
4701         Element *child = children[i];
4702         String tagName = child->getName();
4703         if (tagName == "patternset")
4704             {
4705             if (!parsePatternSet(child, propRef, includes, excludes))
4706                 {
4707                 return false;
4708                 }
4709             }
4710         }
4712     String dir;
4713     //Now do the stuff
4714     //Get the base directory for reading file names
4715     if (!propRef.getAttribute(elem, "dir", dir))
4716         return false;
4718     fileSet.setDirectory(dir);
4719     fileSet.setIncludes(includes);
4720     fileSet.setExcludes(excludes);
4721     
4722     /*
4723     std::vector<String> fileList;
4724     if (dir.size() > 0)
4725         {
4726         String baseDir = propRef.resolve(dir);
4727         if (!listFiles(baseDir, "", includes, excludes, fileList))
4728             return false;
4729         }
4730     std::sort(fileList.begin(), fileList.end());
4731     result = fileList;
4732     */
4734     
4735     /*
4736     for (unsigned int i=0 ; i<result.size() ; i++)
4737         {
4738         trace("RES:%s", result[i].c_str());
4739         }
4740     */
4742     
4743     return true;
4746 /**
4747  * Parse a <filelist> entry.  This is far simpler than FileSet,
4748  * since no directory scanning is needed.  The file names are listed
4749  * explicitly.
4750  */  
4751 bool MakeBase::parseFileList(Element *elem,
4752                           MakeBase &propRef,
4753                           FileList &fileList)
4755     std::vector<String> fnames;
4756     //Look for child tags, namely "file"
4757     std::vector<Element *> children  = elem->getChildren();
4758     for (unsigned int i=0 ; i<children.size() ; i++)
4759         {
4760         Element *child = children[i];
4761         String tagName = child->getName();
4762         if (tagName == "file")
4763             {
4764             String fname = child->getAttribute("name");
4765             if (fname.size()==0)
4766                 {
4767                 error("<file> element requires name="" attribute");
4768                 return false;
4769                 }
4770             fnames.push_back(fname);
4771             }
4772         else
4773             {
4774             error("tag <%s> not allowed in <fileset>", tagName.c_str());
4775             return false;
4776             }
4777         }
4779     String dir;
4780     //Get the base directory for reading file names
4781     if (!propRef.getAttribute(elem, "dir", dir))
4782         return false;
4783     fileList.setDirectory(dir);
4784     fileList.setFiles(fnames);
4786     return true;
4791 /**
4792  * Create a directory, making intermediate dirs
4793  * if necessary
4794  */                  
4795 bool MakeBase::createDirectory(const String &dirname)
4797     //trace("## createDirectory: %s", dirname.c_str());
4798     //## first check if it exists
4799     struct stat finfo;
4800     String nativeDir = getNativePath(dirname);
4801     char *cnative = (char *) nativeDir.c_str();
4802 #ifdef __WIN32__
4803     if (strlen(cnative)==2 && cnative[1]==':')
4804         return true;
4805 #endif
4806     if (stat(cnative, &finfo)==0)
4807         {
4808         if (!S_ISDIR(finfo.st_mode))
4809             {
4810             error("mkdir: file %s exists but is not a directory",
4811                   cnative);
4812             return false;
4813             }
4814         else //exists
4815             {
4816             return true;
4817             }
4818         }
4820     //## 2: pull off the last path segment, if any,
4821     //## to make the dir 'above' this one, if necessary
4822     unsigned int pos = dirname.find_last_of('/');
4823     if (pos>0 && pos != dirname.npos)
4824         {
4825         String subpath = dirname.substr(0, pos);
4826         //A letter root (c:) ?
4827         if (!createDirectory(subpath))
4828             return false;
4829         }
4830         
4831     //## 3: now make
4832 #ifdef __WIN32__
4833     if (mkdir(cnative)<0)
4834 #else
4835     if (mkdir(cnative, S_IRWXU | S_IRWXG | S_IRWXO)<0)
4836 #endif
4837         {
4838         error("cannot make directory '%s' : %s",
4839                  cnative, strerror(errno));
4840         return false;
4841         }
4842         
4843     return true;
4847 /**
4848  * Remove a directory recursively
4849  */ 
4850 bool MakeBase::removeDirectory(const String &dirName)
4852     char *dname = (char *)dirName.c_str();
4854     DIR *dir = opendir(dname);
4855     if (!dir)
4856         {
4857         //# Let this fail nicely.
4858         return true;
4859         //error("error opening directory %s : %s", dname, strerror(errno));
4860         //return false;
4861         }
4862     
4863     while (true)
4864         {
4865         struct dirent *de = readdir(dir);
4866         if (!de)
4867             break;
4869         //Get the directory member name
4870         String s = de->d_name;
4871         if (s.size() == 0 || s[0] == '.')
4872             continue;
4873         String childName;
4874         if (dirName.size() > 0)
4875             {
4876             childName.append(dirName);
4877             childName.append("/");
4878             }
4879         childName.append(s);
4882         struct stat finfo;
4883         String childNative = getNativePath(childName);
4884         char *cnative = (char *)childNative.c_str();
4885         if (stat(cnative, &finfo)<0)
4886             {
4887             error("cannot stat file:%s", cnative);
4888             }
4889         else if (S_ISDIR(finfo.st_mode))
4890             {
4891             //trace("DEL dir: %s", childName.c_str());
4892             if (!removeDirectory(childName))
4893                 {
4894                 return false;
4895                 }
4896             }
4897         else if (!S_ISREG(finfo.st_mode))
4898             {
4899             //trace("not regular: %s", cnative);
4900             }
4901         else
4902             {
4903             //trace("DEL file: %s", childName.c_str());
4904             if (remove(cnative)<0)
4905                 {
4906                 error("error deleting %s : %s",
4907                      cnative, strerror(errno));
4908                 return false;
4909                 }
4910             }
4911         }
4912     closedir(dir);
4914     //Now delete the directory
4915     String native = getNativePath(dirName);
4916     if (rmdir(native.c_str())<0)
4917         {
4918         error("could not delete directory %s : %s",
4919             native.c_str() , strerror(errno));
4920         return false;
4921         }
4923     return true;
4924     
4928 /**
4929  * Copy a file from one name to another. Perform only if needed
4930  */ 
4931 bool MakeBase::copyFile(const String &srcFile, const String &destFile)
4933     //# 1 Check up-to-date times
4934     String srcNative = getNativePath(srcFile);
4935     struct stat srcinfo;
4936     if (stat(srcNative.c_str(), &srcinfo)<0)
4937         {
4938         error("source file %s for copy does not exist",
4939                  srcNative.c_str());
4940         return false;
4941         }
4943     String destNative = getNativePath(destFile);
4944     struct stat destinfo;
4945     if (stat(destNative.c_str(), &destinfo)==0)
4946         {
4947         if (destinfo.st_mtime >= srcinfo.st_mtime)
4948             return true;
4949         }
4950         
4951     //# 2 prepare a destination directory if necessary
4952     unsigned int pos = destFile.find_last_of('/');
4953     if (pos != destFile.npos)
4954         {
4955         String subpath = destFile.substr(0, pos);
4956         if (!createDirectory(subpath))
4957             return false;
4958         }
4960     //# 3 do the data copy
4961 #ifndef __WIN32__
4963     FILE *srcf = fopen(srcNative.c_str(), "rb");
4964     if (!srcf)
4965         {
4966         error("copyFile cannot open '%s' for reading", srcNative.c_str());
4967         return false;
4968         }
4969     FILE *destf = fopen(destNative.c_str(), "wb");
4970     if (!destf)
4971         {
4972         error("copyFile cannot open %s for writing", srcNative.c_str());
4973         return false;
4974         }
4976     while (!feof(srcf))
4977         {
4978         int ch = fgetc(srcf);
4979         if (ch<0)
4980             break;
4981         fputc(ch, destf);
4982         }
4984     fclose(destf);
4985     fclose(srcf);
4987 #else
4988     
4989     if (!CopyFile(srcNative.c_str(), destNative.c_str(), false))
4990         {
4991         error("copyFile from %s to %s failed",
4992              srcNative.c_str(), destNative.c_str());
4993         return false;
4994         }
4995         
4996 #endif /* __WIN32__ */
4999     return true;
5004 /**
5005  * Tests if the file exists and is a regular file
5006  */ 
5007 bool MakeBase::isRegularFile(const String &fileName)
5009     String native = getNativePath(fileName);
5010     struct stat finfo;
5011     
5012     //Exists?
5013     if (stat(native.c_str(), &finfo)<0)
5014         return false;
5017     //check the file mode
5018     if (!S_ISREG(finfo.st_mode))
5019         return false;
5021     return true;
5024 /**
5025  * Tests if the file exists and is a directory
5026  */ 
5027 bool MakeBase::isDirectory(const String &fileName)
5029     String native = getNativePath(fileName);
5030     struct stat finfo;
5031     
5032     //Exists?
5033     if (stat(native.c_str(), &finfo)<0)
5034         return false;
5037     //check the file mode
5038     if (!S_ISDIR(finfo.st_mode))
5039         return false;
5041     return true;
5046 /**
5047  * Tests is the modification of fileA is newer than fileB
5048  */ 
5049 bool MakeBase::isNewerThan(const String &fileA, const String &fileB)
5051     //trace("isNewerThan:'%s' , '%s'", fileA.c_str(), fileB.c_str());
5052     String nativeA = getNativePath(fileA);
5053     struct stat infoA;
5054     //IF source does not exist, NOT newer
5055     if (stat(nativeA.c_str(), &infoA)<0)
5056         {
5057         return false;
5058         }
5060     String nativeB = getNativePath(fileB);
5061     struct stat infoB;
5062     //IF dest does not exist, YES, newer
5063     if (stat(nativeB.c_str(), &infoB)<0)
5064         {
5065         return true;
5066         }
5068     //check the actual times
5069     if (infoA.st_mtime > infoB.st_mtime)
5070         {
5071         return true;
5072         }
5074     return false;
5078 //########################################################################
5079 //# P K G    C O N F I G
5080 //########################################################################
5083 /**
5084  * Get a character from the buffer at pos.  If out of range,
5085  * return -1 for safety
5086  */
5087 int PkgConfig::get(int pos)
5089     if (pos>parselen)
5090         return -1;
5091     return parsebuf[pos];
5096 /**
5097  *  Skip over all whitespace characters beginning at pos.  Return
5098  *  the position of the first non-whitespace character.
5099  *  Pkg-config is line-oriented, so check for newline
5100  */
5101 int PkgConfig::skipwhite(int pos)
5103     while (pos < parselen)
5104         {
5105         int ch = get(pos);
5106         if (ch < 0)
5107             break;
5108         if (!isspace(ch))
5109             break;
5110         pos++;
5111         }
5112     return pos;
5116 /**
5117  *  Parse the buffer beginning at pos, for a word.  Fill
5118  *  'ret' with the result.  Return the position after the
5119  *  word.
5120  */
5121 int PkgConfig::getword(int pos, String &ret)
5123     while (pos < parselen)
5124         {
5125         int ch = get(pos);
5126         if (ch < 0)
5127             break;
5128         if (!isalnum(ch) && ch != '_' && ch != '-' && ch != '+' && ch != '.')
5129             break;
5130         ret.push_back((char)ch);
5131         pos++;
5132         }
5133     return pos;
5136 bool PkgConfig::parseRequires()
5138     if (requires.size() == 0)
5139         return true;
5140     parsebuf = (char *)requires.c_str();
5141     parselen = requires.size();
5142     int pos = 0;
5143     while (pos < parselen)
5144         {
5145         pos = skipwhite(pos);
5146         String val;
5147         int pos2 = getword(pos, val);
5148         if (pos2 == pos)
5149             break;
5150         pos = pos2;
5151         //trace("val %s", val.c_str());
5152         requireList.push_back(val);
5153         }
5154     return true;
5158 static int getint(const String str)
5160     char *s = (char *)str.c_str();
5161     char *ends = NULL;
5162     long val = strtol(s, &ends, 10);
5163     if (ends == s)
5164         return 0L;
5165     else
5166         return val;
5169 void PkgConfig::parseVersion()
5171     if (version.size() == 0)
5172         return;
5173     String s1, s2, s3;
5174     unsigned int pos = 0;
5175     unsigned int pos2 = version.find('.', pos);
5176     if (pos2 == version.npos)
5177         {
5178         s1 = version;
5179         }
5180     else
5181         {
5182         s1 = version.substr(pos, pos2-pos);
5183         pos = pos2;
5184         pos++;
5185         if (pos < version.size())
5186             {
5187             pos2 = version.find('.', pos);
5188             if (pos2 == version.npos)
5189                 {
5190                 s2 = version.substr(pos, version.size()-pos);
5191                 }
5192             else
5193                 {
5194                 s2 = version.substr(pos, pos2-pos);
5195                 pos = pos2;
5196                 pos++;
5197                 if (pos < version.size())
5198                     s3 = version.substr(pos, pos2-pos);
5199                 }
5200             }
5201         }
5203     majorVersion = getint(s1);
5204     minorVersion = getint(s2);
5205     microVersion = getint(s3);
5206     //trace("version:%d.%d.%d", majorVersion,
5207     //          minorVersion, microVersion );
5211 bool PkgConfig::parseLine(const String &lineBuf)
5213     parsebuf = (char *)lineBuf.c_str();
5214     parselen = lineBuf.size();
5215     int pos = 0;
5216     
5217     while (pos < parselen)
5218         {
5219         String attrName;
5220         pos = skipwhite(pos);
5221         int ch = get(pos);
5222         if (ch == '#')
5223             {
5224             //comment.  eat the rest of the line
5225             while (pos < parselen)
5226                 {
5227                 ch = get(pos);
5228                 if (ch == '\n' || ch < 0)
5229                     break;
5230                 pos++;
5231                 }
5232             continue;
5233             }
5234         pos = getword(pos, attrName);
5235         if (attrName.size() == 0)
5236             continue;
5237         
5238         pos = skipwhite(pos);
5239         ch = get(pos);
5240         if (ch != ':' && ch != '=')
5241             {
5242             error("expected ':' or '='");
5243             return false;
5244             }
5245         pos++;
5246         pos = skipwhite(pos);
5247         String attrVal;
5248         while (pos < parselen)
5249             {
5250             ch = get(pos);
5251             if (ch == '\n' || ch < 0)
5252                 break;
5253             else if (ch == '$' && get(pos+1) == '{')
5254                 {
5255                 //#  this is a ${substitution}
5256                 pos += 2;
5257                 String subName;
5258                 while (pos < parselen)
5259                     {
5260                     ch = get(pos);
5261                     if (ch < 0)
5262                         {
5263                         error("unterminated substitution");
5264                         return false;
5265                         }
5266                     else if (ch == '}')
5267                         break;
5268                     else
5269                         subName.push_back((char)ch);
5270                     pos++;
5271                     }
5272                 //trace("subName:%s %s", subName.c_str(), prefix.c_str());
5273                 if (subName == "prefix" && prefix.size()>0)
5274                     {
5275                     attrVal.append(prefix);
5276                     //trace("prefix override:%s", prefix.c_str());
5277                     }
5278                 else
5279                     {
5280                     String subVal = attrs[subName];
5281                     //trace("subVal:%s", subVal.c_str());
5282                     attrVal.append(subVal);
5283                     }
5284                 }
5285             else
5286                 attrVal.push_back((char)ch);
5287             pos++;
5288             }
5290         attrVal = trim(attrVal);
5291         attrs[attrName] = attrVal;
5293         String attrNameL = toLower(attrName);
5295         if (attrNameL == "name")
5296             name = attrVal;
5297         else if (attrNameL == "description")
5298             description = attrVal;
5299         else if (attrNameL == "cflags")
5300             cflags = attrVal;
5301         else if (attrNameL == "libs")
5302             libs = attrVal;
5303         else if (attrNameL == "requires")
5304             requires = attrVal;
5305         else if (attrNameL == "version")
5306             version = attrVal;
5308         //trace("name:'%s'  value:'%s'",
5309         //      attrName.c_str(), attrVal.c_str());
5310         }
5312     return true;
5316 bool PkgConfig::parse(const String &buf)
5318     init();
5320     String line;
5321     int lineNr = 0;
5322     for (unsigned int p=0 ; p<buf.size() ; p++)
5323         {
5324         int ch = buf[p];
5325         if (ch == '\n' || ch == '\r')
5326             {
5327             if (!parseLine(line))
5328                 return false;
5329             line.clear();
5330             lineNr++;
5331             }
5332         else
5333             {
5334             line.push_back(ch);
5335             }
5336         }
5337     if (line.size()>0)
5338         {
5339         if (!parseLine(line))
5340             return false;
5341         }
5343     parseRequires();
5344     parseVersion();
5346     return true;
5352 void PkgConfig::dumpAttrs()
5354     //trace("### PkgConfig attributes for %s", fileName.c_str());
5355     std::map<String, String>::iterator iter;
5356     for (iter=attrs.begin() ; iter!=attrs.end() ; iter++)
5357         {
5358         trace("   %s = %s", iter->first.c_str(), iter->second.c_str());
5359         }
5363 bool PkgConfig::readFile(const String &fname)
5365     fileName = getNativePath(fname);
5367     FILE *f = fopen(fileName.c_str(), "r");
5368     if (!f)
5369         {
5370         error("cannot open file '%s' for reading", fileName.c_str());
5371         return false;
5372         }
5373     String buf;
5374     while (true)
5375         {
5376         int ch = fgetc(f);
5377         if (ch < 0)
5378             break;
5379         buf.push_back((char)ch);
5380         }
5381     fclose(f);
5383     //trace("####### File:\n%s", buf.c_str());
5384     if (!parse(buf))
5385         {
5386         return false;
5387         }
5389     //dumpAttrs();
5391     return true;
5396 bool PkgConfig::query(const String &pkgName)
5398     name = pkgName;
5400     String fname = path;
5401     fname.append("/");
5402     fname.append(name);
5403     fname.append(".pc");
5405     if (!readFile(fname))
5406         return false;
5407     
5408     return true;
5415 //########################################################################
5416 //# D E P T O O L
5417 //########################################################################
5421 /**
5422  *  Class which holds information for each file.
5423  */
5424 class FileRec
5426 public:
5428     typedef enum
5429         {
5430         UNKNOWN,
5431         CFILE,
5432         HFILE,
5433         OFILE
5434         } FileType;
5436     /**
5437      *  Constructor
5438      */
5439     FileRec()
5440         { init(); type = UNKNOWN; }
5442     /**
5443      *  Copy constructor
5444      */
5445     FileRec(const FileRec &other)
5446         { init(); assign(other); }
5447     /**
5448      *  Constructor
5449      */
5450     FileRec(int typeVal)
5451         { init(); type = typeVal; }
5452     /**
5453      *  Assignment operator
5454      */
5455     FileRec &operator=(const FileRec &other)
5456         { init(); assign(other); return *this; }
5459     /**
5460      *  Destructor
5461      */
5462     ~FileRec()
5463         {}
5465     /**
5466      *  Directory part of the file name
5467      */
5468     String path;
5470     /**
5471      *  Base name, sans directory and suffix
5472      */
5473     String baseName;
5475     /**
5476      *  File extension, such as cpp or h
5477      */
5478     String suffix;
5480     /**
5481      *  Type of file: CFILE, HFILE, OFILE
5482      */
5483     int type;
5485     /**
5486      * Used to list files ref'd by this one
5487      */
5488     std::map<String, FileRec *> files;
5491 private:
5493     void init()
5494         {
5495         }
5497     void assign(const FileRec &other)
5498         {
5499         type     = other.type;
5500         baseName = other.baseName;
5501         suffix   = other.suffix;
5502         files    = other.files;
5503         }
5505 };
5509 /**
5510  *  Simpler dependency record
5511  */
5512 class DepRec
5514 public:
5516     /**
5517      *  Constructor
5518      */
5519     DepRec()
5520         {init();}
5522     /**
5523      *  Copy constructor
5524      */
5525     DepRec(const DepRec &other)
5526         {init(); assign(other);}
5527     /**
5528      *  Constructor
5529      */
5530     DepRec(const String &fname)
5531         {init(); name = fname; }
5532     /**
5533      *  Assignment operator
5534      */
5535     DepRec &operator=(const DepRec &other)
5536         {init(); assign(other); return *this;}
5539     /**
5540      *  Destructor
5541      */
5542     ~DepRec()
5543         {}
5545     /**
5546      *  Directory part of the file name
5547      */
5548     String path;
5550     /**
5551      *  Base name, without the path and suffix
5552      */
5553     String name;
5555     /**
5556      *  Suffix of the source
5557      */
5558     String suffix;
5561     /**
5562      * Used to list files ref'd by this one
5563      */
5564     std::vector<String> files;
5567 private:
5569     void init()
5570         {
5571         }
5573     void assign(const DepRec &other)
5574         {
5575         path     = other.path;
5576         name     = other.name;
5577         suffix   = other.suffix;
5578         files    = other.files; //avoid recursion
5579         }
5581 };
5584 class DepTool : public MakeBase
5586 public:
5588     /**
5589      *  Constructor
5590      */
5591     DepTool()
5592         { init(); }
5594     /**
5595      *  Copy constructor
5596      */
5597     DepTool(const DepTool &other)
5598         { init(); assign(other); }
5600     /**
5601      *  Assignment operator
5602      */
5603     DepTool &operator=(const DepTool &other)
5604         { init(); assign(other); return *this; }
5607     /**
5608      *  Destructor
5609      */
5610     ~DepTool()
5611         {}
5614     /**
5615      *  Reset this section of code
5616      */
5617     virtual void init();
5618     
5619     /**
5620      *  Reset this section of code
5621      */
5622     virtual void assign(const DepTool &other)
5623         {
5624         }
5625     
5626     /**
5627      *  Sets the source directory which will be scanned
5628      */
5629     virtual void setSourceDirectory(const String &val)
5630         { sourceDir = val; }
5632     /**
5633      *  Returns the source directory which will be scanned
5634      */
5635     virtual String getSourceDirectory()
5636         { return sourceDir; }
5638     /**
5639      *  Sets the list of files within the directory to analyze
5640      */
5641     virtual void setFileList(const std::vector<String> &list)
5642         { fileList = list; }
5644     /**
5645      * Creates the list of all file names which will be
5646      * candidates for further processing.  Reads make.exclude
5647      * to see which files for directories to leave out.
5648      */
5649     virtual bool createFileList();
5652     /**
5653      *  Generates the forward dependency list
5654      */
5655     virtual bool generateDependencies();
5658     /**
5659      *  Generates the forward dependency list, saving the file
5660      */
5661     virtual bool generateDependencies(const String &);
5664     /**
5665      *  Load a dependency file
5666      */
5667     std::vector<DepRec> loadDepFile(const String &fileName);
5669     /**
5670      *  Load a dependency file, generating one if necessary
5671      */
5672     std::vector<DepRec> getDepFile(const String &fileName,
5673               bool forceRefresh);
5675     /**
5676      *  Save a dependency file
5677      */
5678     bool saveDepFile(const String &fileName);
5681 private:
5684     /**
5685      *
5686      */
5687     void parseName(const String &fullname,
5688                    String &path,
5689                    String &basename,
5690                    String &suffix);
5692     /**
5693      *
5694      */
5695     int get(int pos);
5697     /**
5698      *
5699      */
5700     int skipwhite(int pos);
5702     /**
5703      *
5704      */
5705     int getword(int pos, String &ret);
5707     /**
5708      *
5709      */
5710     bool sequ(int pos, const char *key);
5712     /**
5713      *
5714      */
5715     bool addIncludeFile(FileRec *frec, const String &fname);
5717     /**
5718      *
5719      */
5720     bool scanFile(const String &fname, FileRec *frec);
5722     /**
5723      *
5724      */
5725     bool processDependency(FileRec *ofile, FileRec *include);
5727     /**
5728      *
5729      */
5730     String sourceDir;
5732     /**
5733      *
5734      */
5735     std::vector<String> fileList;
5737     /**
5738      *
5739      */
5740     std::vector<String> directories;
5742     /**
5743      * A list of all files which will be processed for
5744      * dependencies.
5745      */
5746     std::map<String, FileRec *> allFiles;
5748     /**
5749      * The list of .o files, and the
5750      * dependencies upon them.
5751      */
5752     std::map<String, FileRec *> oFiles;
5754     int depFileSize;
5755     char *depFileBuf;
5757     static const int readBufSize = 8192;
5758     char readBuf[8193];//byte larger
5760 };
5766 /**
5767  *  Clean up after processing.  Called by the destructor, but should
5768  *  also be called before the object is reused.
5769  */
5770 void DepTool::init()
5772     sourceDir = ".";
5774     fileList.clear();
5775     directories.clear();
5776     
5777     //clear output file list
5778     std::map<String, FileRec *>::iterator iter;
5779     for (iter=oFiles.begin(); iter!=oFiles.end() ; iter++)
5780         delete iter->second;
5781     oFiles.clear();
5783     //allFiles actually contains the master copies. delete them
5784     for (iter= allFiles.begin(); iter!=allFiles.end() ; iter++)
5785         delete iter->second;
5786     allFiles.clear(); 
5793 /**
5794  *  Parse a full path name into path, base name, and suffix
5795  */
5796 void DepTool::parseName(const String &fullname,
5797                         String &path,
5798                         String &basename,
5799                         String &suffix)
5801     if (fullname.size() < 2)
5802         return;
5804     unsigned int pos = fullname.find_last_of('/');
5805     if (pos != fullname.npos && pos<fullname.size()-1)
5806         {
5807         path = fullname.substr(0, pos);
5808         pos++;
5809         basename = fullname.substr(pos, fullname.size()-pos);
5810         }
5811     else
5812         {
5813         path = "";
5814         basename = fullname;
5815         }
5817     pos = basename.find_last_of('.');
5818     if (pos != basename.npos && pos<basename.size()-1)
5819         {
5820         suffix   = basename.substr(pos+1, basename.size()-pos-1);
5821         basename = basename.substr(0, pos);
5822         }
5824     //trace("parsename:%s %s %s", path.c_str(),
5825     //        basename.c_str(), suffix.c_str()); 
5830 /**
5831  *  Generate our internal file list.
5832  */
5833 bool DepTool::createFileList()
5836     for (unsigned int i=0 ; i<fileList.size() ; i++)
5837         {
5838         String fileName = fileList[i];
5839         //trace("## FileName:%s", fileName.c_str());
5840         String path;
5841         String basename;
5842         String sfx;
5843         parseName(fileName, path, basename, sfx);
5844         if (sfx == "cpp" || sfx == "c" || sfx == "cxx"   ||
5845             sfx == "cc" || sfx == "CC")
5846             {
5847             FileRec *fe         = new FileRec(FileRec::CFILE);
5848             fe->path            = path;
5849             fe->baseName        = basename;
5850             fe->suffix          = sfx;
5851             allFiles[fileName]  = fe;
5852             }
5853         else if (sfx == "h"   ||  sfx == "hh"  ||
5854                  sfx == "hpp" ||  sfx == "hxx")
5855             {
5856             FileRec *fe         = new FileRec(FileRec::HFILE);
5857             fe->path            = path;
5858             fe->baseName        = basename;
5859             fe->suffix          = sfx;
5860             allFiles[fileName]  = fe;
5861             }
5862         }
5864     if (!listDirectories(sourceDir, "", directories))
5865         return false;
5866         
5867     return true;
5874 /**
5875  * Get a character from the buffer at pos.  If out of range,
5876  * return -1 for safety
5877  */
5878 int DepTool::get(int pos)
5880     if (pos>depFileSize)
5881         return -1;
5882     return depFileBuf[pos];
5887 /**
5888  *  Skip over all whitespace characters beginning at pos.  Return
5889  *  the position of the first non-whitespace character.
5890  */
5891 int DepTool::skipwhite(int pos)
5893     while (pos < depFileSize)
5894         {
5895         int ch = get(pos);
5896         if (ch < 0)
5897             break;
5898         if (!isspace(ch))
5899             break;
5900         pos++;
5901         }
5902     return pos;
5906 /**
5907  *  Parse the buffer beginning at pos, for a word.  Fill
5908  *  'ret' with the result.  Return the position after the
5909  *  word.
5910  */
5911 int DepTool::getword(int pos, String &ret)
5913     while (pos < depFileSize)
5914         {
5915         int ch = get(pos);
5916         if (ch < 0)
5917             break;
5918         if (isspace(ch))
5919             break;
5920         ret.push_back((char)ch);
5921         pos++;
5922         }
5923     return pos;
5926 /**
5927  * Return whether the sequence of characters in the buffer
5928  * beginning at pos match the key,  for the length of the key
5929  */
5930 bool DepTool::sequ(int pos, const char *key)
5932     while (*key)
5933         {
5934         if (*key != get(pos))
5935             return false;
5936         key++; pos++;
5937         }
5938     return true;
5943 /**
5944  *  Add an include file name to a file record.  If the name
5945  *  is not found in allFiles explicitly, try prepending include
5946  *  directory names to it and try again.
5947  */
5948 bool DepTool::addIncludeFile(FileRec *frec, const String &iname)
5950     //# if the name is an exact match to a path name
5951     //# in allFiles, like "myinc.h"
5952     std::map<String, FileRec *>::iterator iter =
5953            allFiles.find(iname);
5954     if (iter != allFiles.end()) //already exists
5955         {
5956          //h file in same dir
5957         FileRec *other = iter->second;
5958         //trace("local: '%s'", iname.c_str());
5959         frec->files[iname] = other;
5960         return true;
5961         }
5962     else 
5963         {
5964         //## Ok, it was not found directly
5965         //look in other dirs
5966         std::vector<String>::iterator diter;
5967         for (diter=directories.begin() ;
5968              diter!=directories.end() ; diter++)
5969             {
5970             String dfname = *diter;
5971             dfname.append("/");
5972             dfname.append(iname);
5973             URI fullPathURI(dfname);  //normalize path name
5974             String fullPath = fullPathURI.getPath();
5975             if (fullPath[0] == '/')
5976                 fullPath = fullPath.substr(1);
5977             //trace("Normalized %s to %s", dfname.c_str(), fullPath.c_str());
5978             iter = allFiles.find(fullPath);
5979             if (iter != allFiles.end())
5980                 {
5981                 FileRec *other = iter->second;
5982                 //trace("other: '%s'", iname.c_str());
5983                 frec->files[fullPath] = other;
5984                 return true;
5985                 }
5986             }
5987         }
5988     return true;
5993 /**
5994  *  Lightly parse a file to find the #include directives.  Do
5995  *  a bit of state machine stuff to make sure that the directive
5996  *  is valid.  (Like not in a comment).
5997  */
5998 bool DepTool::scanFile(const String &fname, FileRec *frec)
6000     String fileName;
6001     if (sourceDir.size() > 0)
6002         {
6003         fileName.append(sourceDir);
6004         fileName.append("/");
6005         }
6006     fileName.append(fname);
6007     String nativeName = getNativePath(fileName);
6008     FILE *f = fopen(nativeName.c_str(), "r");
6009     if (!f)
6010         {
6011         error("Could not open '%s' for reading", fname.c_str());
6012         return false;
6013         }
6014     String buf;
6015     while (!feof(f))
6016         {
6017         int nrbytes = fread(readBuf, 1, readBufSize, f);
6018         readBuf[nrbytes] = '\0';
6019         buf.append(readBuf);
6020         }
6021     fclose(f);
6023     depFileSize = buf.size();
6024     depFileBuf  = (char *)buf.c_str();
6025     int pos = 0;
6028     while (pos < depFileSize)
6029         {
6030         //trace("p:%c", get(pos));
6032         //# Block comment
6033         if (get(pos) == '/' && get(pos+1) == '*')
6034             {
6035             pos += 2;
6036             while (pos < depFileSize)
6037                 {
6038                 if (get(pos) == '*' && get(pos+1) == '/')
6039                     {
6040                     pos += 2;
6041                     break;
6042                     }
6043                 else
6044                     pos++;
6045                 }
6046             }
6047         //# Line comment
6048         else if (get(pos) == '/' && get(pos+1) == '/')
6049             {
6050             pos += 2;
6051             while (pos < depFileSize)
6052                 {
6053                 if (get(pos) == '\n')
6054                     {
6055                     pos++;
6056                     break;
6057                     }
6058                 else
6059                     pos++;
6060                 }
6061             }
6062         //# #include! yaay
6063         else if (sequ(pos, "#include"))
6064             {
6065             pos += 8;
6066             pos = skipwhite(pos);
6067             String iname;
6068             pos = getword(pos, iname);
6069             if (iname.size()>2)
6070                 {
6071                 iname = iname.substr(1, iname.size()-2);
6072                 addIncludeFile(frec, iname);
6073                 }
6074             }
6075         else
6076             {
6077             pos++;
6078             }
6079         }
6081     return true;
6086 /**
6087  *  Recursively check include lists to find all files in allFiles to which
6088  *  a given file is dependent.
6089  */
6090 bool DepTool::processDependency(FileRec *ofile, FileRec *include)
6092     std::map<String, FileRec *>::iterator iter;
6093     for (iter=include->files.begin() ; iter!=include->files.end() ; iter++)
6094         {
6095         String fname  = iter->first;
6096         if (ofile->files.find(fname) != ofile->files.end())
6097             {
6098             //trace("file '%s' already seen", fname.c_str());
6099             continue;
6100             }
6101         FileRec *child  = iter->second;
6102         ofile->files[fname] = child;
6103       
6104         processDependency(ofile, child);
6105         }
6108     return true;
6115 /**
6116  *  Generate the file dependency list.
6117  */
6118 bool DepTool::generateDependencies()
6120     std::map<String, FileRec *>::iterator iter;
6121     //# First pass.  Scan for all includes
6122     for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
6123         {
6124         FileRec *frec = iter->second;
6125         if (!scanFile(iter->first, frec))
6126             {
6127             //quit?
6128             }
6129         }
6131     //# Second pass.  Scan for all includes
6132     for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
6133         {
6134         FileRec *include = iter->second;
6135         if (include->type == FileRec::CFILE)
6136             {
6137             //String cFileName   = iter->first;
6138             FileRec *ofile     = new FileRec(FileRec::OFILE);
6139             ofile->path        = include->path;
6140             ofile->baseName    = include->baseName;
6141             ofile->suffix      = include->suffix;
6142             String fname       = include->path;
6143             if (fname.size()>0)
6144                 fname.append("/");
6145             fname.append(include->baseName);
6146             fname.append(".o");
6147             oFiles[fname]    = ofile;
6148             //add the .c file first?   no, don't
6149             //ofile->files[cFileName] = include;
6150             
6151             //trace("ofile:%s", fname.c_str());
6153             processDependency(ofile, include);
6154             }
6155         }
6157       
6158     return true;
6163 /**
6164  *  High-level call to generate deps and optionally save them
6165  */
6166 bool DepTool::generateDependencies(const String &fileName)
6168     if (!createFileList())
6169         return false;
6170     if (!generateDependencies())
6171         return false;
6172     if (!saveDepFile(fileName))
6173         return false;
6174     return true;
6178 /**
6179  *   This saves the dependency cache.
6180  */
6181 bool DepTool::saveDepFile(const String &fileName)
6183     time_t tim;
6184     time(&tim);
6186     FILE *f = fopen(fileName.c_str(), "w");
6187     if (!f)
6188         {
6189         trace("cannot open '%s' for writing", fileName.c_str());
6190         }
6191     fprintf(f, "<?xml version='1.0'?>\n");
6192     fprintf(f, "<!--\n");
6193     fprintf(f, "########################################################\n");
6194     fprintf(f, "## File: build.dep\n");
6195     fprintf(f, "## Generated by BuildTool at :%s", ctime(&tim));
6196     fprintf(f, "########################################################\n");
6197     fprintf(f, "-->\n");
6199     fprintf(f, "<dependencies source='%s'>\n\n", sourceDir.c_str());
6200     std::map<String, FileRec *>::iterator iter;
6201     for (iter=oFiles.begin() ; iter!=oFiles.end() ; iter++)
6202         {
6203         FileRec *frec = iter->second;
6204         if (frec->type == FileRec::OFILE)
6205             {
6206             fprintf(f, "<object path='%s' name='%s' suffix='%s'>\n",
6207                  frec->path.c_str(), frec->baseName.c_str(), frec->suffix.c_str());
6208             std::map<String, FileRec *>::iterator citer;
6209             for (citer=frec->files.begin() ; citer!=frec->files.end() ; citer++)
6210                 {
6211                 String cfname = citer->first;
6212                 fprintf(f, "    <dep name='%s'/>\n", cfname.c_str());
6213                 }
6214             fprintf(f, "</object>\n\n");
6215             }
6216         }
6218     fprintf(f, "</dependencies>\n");
6219     fprintf(f, "\n");
6220     fprintf(f, "<!--\n");
6221     fprintf(f, "########################################################\n");
6222     fprintf(f, "## E N D\n");
6223     fprintf(f, "########################################################\n");
6224     fprintf(f, "-->\n");
6226     fclose(f);
6228     return true;
6234 /**
6235  *   This loads the dependency cache.
6236  */
6237 std::vector<DepRec> DepTool::loadDepFile(const String &depFile)
6239     std::vector<DepRec> result;
6240     
6241     Parser parser;
6242     Element *root = parser.parseFile(depFile.c_str());
6243     if (!root)
6244         {
6245         //error("Could not open %s for reading", depFile.c_str());
6246         return result;
6247         }
6249     if (root->getChildren().size()==0 ||
6250         root->getChildren()[0]->getName()!="dependencies")
6251         {
6252         error("loadDepFile: main xml element should be <dependencies>");
6253         delete root;
6254         return result;
6255         }
6257     //########## Start parsing
6258     Element *depList = root->getChildren()[0];
6260     std::vector<Element *> objects = depList->getChildren();
6261     for (unsigned int i=0 ; i<objects.size() ; i++)
6262         {
6263         Element *objectElem = objects[i];
6264         String tagName = objectElem->getName();
6265         if (tagName != "object")
6266             {
6267             error("loadDepFile: <dependencies> should have only <object> children");
6268             return result;
6269             }
6271         String objName   = objectElem->getAttribute("name");
6272          //trace("object:%s", objName.c_str());
6273         DepRec depObject(objName);
6274         depObject.path   = objectElem->getAttribute("path");
6275         depObject.suffix = objectElem->getAttribute("suffix");
6276         //########## DESCRIPTION
6277         std::vector<Element *> depElems = objectElem->getChildren();
6278         for (unsigned int i=0 ; i<depElems.size() ; i++)
6279             {
6280             Element *depElem = depElems[i];
6281             tagName = depElem->getName();
6282             if (tagName != "dep")
6283                 {
6284                 error("loadDepFile: <object> should have only <dep> children");
6285                 return result;
6286                 }
6287             String depName = depElem->getAttribute("name");
6288             //trace("    dep:%s", depName.c_str());
6289             depObject.files.push_back(depName);
6290             }
6292         //Insert into the result list, in a sorted manner
6293         bool inserted = false;
6294         std::vector<DepRec>::iterator iter;
6295         for (iter = result.begin() ; iter != result.end() ; iter++)
6296             {
6297             String vpath = iter->path;
6298             vpath.append("/");
6299             vpath.append(iter->name);
6300             String opath = depObject.path;
6301             opath.append("/");
6302             opath.append(depObject.name);
6303             if (vpath > opath)
6304                 {
6305                 inserted = true;
6306                 iter = result.insert(iter, depObject);
6307                 break;
6308                 }
6309             }
6310         if (!inserted)
6311             result.push_back(depObject);
6312         }
6314     delete root;
6316     return result;
6320 /**
6321  *   This loads the dependency cache.
6322  */
6323 std::vector<DepRec> DepTool::getDepFile(const String &depFile,
6324                    bool forceRefresh)
6326     std::vector<DepRec> result;
6327     if (forceRefresh)
6328         {
6329         generateDependencies(depFile);
6330         result = loadDepFile(depFile);
6331         }
6332     else
6333         {
6334         //try once
6335         result = loadDepFile(depFile);
6336         if (result.size() == 0)
6337             {
6338             //fail? try again
6339             generateDependencies(depFile);
6340             result = loadDepFile(depFile);
6341             }
6342         }
6343     return result;
6349 //########################################################################
6350 //# T A S K
6351 //########################################################################
6352 //forward decl
6353 class Target;
6354 class Make;
6356 /**
6357  *
6358  */
6359 class Task : public MakeBase
6362 public:
6364     typedef enum
6365         {
6366         TASK_NONE,
6367         TASK_CC,
6368         TASK_COPY,
6369         TASK_DELETE,
6370         TASK_ECHO,
6371         TASK_JAR,
6372         TASK_JAVAC,
6373         TASK_LINK,
6374         TASK_MAKEFILE,
6375         TASK_MKDIR,
6376         TASK_MSGFMT,
6377         TASK_PKG_CONFIG,
6378         TASK_RANLIB,
6379         TASK_RC,
6380         TASK_SHAREDLIB,
6381         TASK_STATICLIB,
6382         TASK_STRIP,
6383         TASK_TOUCH,
6384         TASK_TSTAMP
6385         } TaskType;
6386         
6388     /**
6389      *
6390      */
6391     Task(MakeBase &par) : parent(par)
6392         { init(); }
6394     /**
6395      *
6396      */
6397     Task(const Task &other) : parent(other.parent)
6398         { init(); assign(other); }
6400     /**
6401      *
6402      */
6403     Task &operator=(const Task &other)
6404         { assign(other); return *this; }
6406     /**
6407      *
6408      */
6409     virtual ~Task()
6410         { }
6413     /**
6414      *
6415      */
6416     virtual MakeBase &getParent()
6417         { return parent; }
6419      /**
6420      *
6421      */
6422     virtual int  getType()
6423         { return type; }
6425     /**
6426      *
6427      */
6428     virtual void setType(int val)
6429         { type = val; }
6431     /**
6432      *
6433      */
6434     virtual String getName()
6435         { return name; }
6437     /**
6438      *
6439      */
6440     virtual bool execute()
6441         { return true; }
6443     /**
6444      *
6445      */
6446     virtual bool parse(Element *elem)
6447         { return true; }
6449     /**
6450      *
6451      */
6452     Task *createTask(Element *elem, int lineNr);
6455 protected:
6457     void init()
6458         {
6459         type = TASK_NONE;
6460         name = "none";
6461         }
6463     void assign(const Task &other)
6464         {
6465         type = other.type;
6466         name = other.name;
6467         }
6468         
6469     /**
6470      *  Show task status
6471      */
6472     void taskstatus(const char *fmt, ...)
6473         {
6474         va_list args;
6475         va_start(args,fmt);
6476         fprintf(stdout, "    %s : ", name.c_str());
6477         vfprintf(stdout, fmt, args);
6478         fprintf(stdout, "\n");
6479         va_end(args) ;
6480         }
6482     String getAttribute(Element *elem, const String &attrName)
6483         {
6484         String str;
6485         return str;
6486         }
6488     MakeBase &parent;
6490     int type;
6492     String name;
6493 };
6497 /**
6498  * This task runs the C/C++ compiler.  The compiler is invoked
6499  * for all .c or .cpp files which are newer than their correcsponding
6500  * .o files.  
6501  */
6502 class TaskCC : public Task
6504 public:
6506     TaskCC(MakeBase &par) : Task(par)
6507         {
6508         type = TASK_CC;
6509         name = "cc";
6510         }
6512     virtual ~TaskCC()
6513         {}
6514         
6515     virtual bool isExcludedInc(const String &dirname)
6516         {
6517         for (unsigned int i=0 ; i<excludeInc.size() ; i++)
6518             {
6519             String fname = excludeInc[i];
6520             if (fname == dirname)
6521                 return true;
6522             }
6523         return false;
6524         }
6526     virtual bool execute()
6527         {
6528         //evaluate our parameters
6529         String command         = parent.eval(commandOpt, "gcc");
6530         String ccCommand       = parent.eval(ccCommandOpt, "gcc");
6531         String cxxCommand      = parent.eval(cxxCommandOpt, "g++");
6532         String source          = parent.eval(sourceOpt, ".");
6533         String dest            = parent.eval(destOpt, ".");
6534         String flags           = parent.eval(flagsOpt, "");
6535         String defines         = parent.eval(definesOpt, "");
6536         String includes        = parent.eval(includesOpt, "");
6537         bool continueOnError   = parent.evalBool(continueOnErrorOpt, true);
6538         bool refreshCache      = parent.evalBool(refreshCacheOpt, false);
6540         if (!listFiles(parent, fileSet))
6541             return false;
6542             
6543         FILE *f = NULL;
6544         f = fopen("compile.lst", "w");
6546         //refreshCache is probably false here, unless specified otherwise
6547         String fullName = parent.resolve("build.dep");
6548         if (refreshCache || isNewerThan(parent.getURI().getPath(), fullName))
6549             {
6550             taskstatus("regenerating C/C++ dependency cache");
6551             refreshCache = true;
6552             }
6554         DepTool depTool;
6555         depTool.setSourceDirectory(source);
6556         depTool.setFileList(fileSet.getFiles());
6557         std::vector<DepRec> deps =
6558              depTool.getDepFile("build.dep", refreshCache);
6559         
6560         String incs;
6561         incs.append("-I");
6562         incs.append(parent.resolve("."));
6563         incs.append(" ");
6564         if (includes.size()>0)
6565             {
6566             incs.append(includes);
6567             incs.append(" ");
6568             }
6569         std::set<String> paths;
6570         std::vector<DepRec>::iterator viter;
6571         for (viter=deps.begin() ; viter!=deps.end() ; viter++)
6572             {
6573             DepRec dep = *viter;
6574             if (dep.path.size()>0)
6575                 paths.insert(dep.path);
6576             }
6577         if (source.size()>0)
6578             {
6579             incs.append(" -I");
6580             incs.append(parent.resolve(source));
6581             incs.append(" ");
6582             }
6583         std::set<String>::iterator setIter;
6584         for (setIter=paths.begin() ; setIter!=paths.end() ; setIter++)
6585             {
6586             String dirName = *setIter;
6587             //check excludeInc to see if we dont want to include this dir
6588             if (isExcludedInc(dirName))
6589                 continue;
6590             incs.append(" -I");
6591             String dname;
6592             if (source.size()>0)
6593                 {
6594                 dname.append(source);
6595                 dname.append("/");
6596                 }
6597             dname.append(dirName);
6598             incs.append(parent.resolve(dname));
6599             }
6600             
6601         /**
6602          * Compile each of the C files that need it
6603          */
6604         bool errorOccurred = false;                 
6605         std::vector<String> cfiles;
6606         for (viter=deps.begin() ; viter!=deps.end() ; viter++)
6607             {
6608             DepRec dep = *viter;
6610             //## Select command
6611             String sfx = dep.suffix;
6612             String command = ccCommand;
6613             if (sfx == "cpp" || sfx == "cxx" || sfx == "c++" ||
6614                  sfx == "cc" || sfx == "CC")
6615                 command = cxxCommand;
6616  
6617             //## Make paths
6618             String destPath = dest;
6619             String srcPath  = source;
6620             if (dep.path.size()>0)
6621                 {
6622                 destPath.append("/");
6623                 destPath.append(dep.path);
6624                 srcPath.append("/");
6625                 srcPath.append(dep.path);
6626                 }
6627             //## Make sure destination directory exists
6628             if (!createDirectory(destPath))
6629                 return false;
6630                 
6631             //## Check whether it needs to be done
6632             String destName;
6633             if (destPath.size()>0)
6634                 {
6635                 destName.append(destPath);
6636                 destName.append("/");
6637                 }
6638             destName.append(dep.name);
6639             destName.append(".o");
6640             String destFullName = parent.resolve(destName);
6641             String srcName;
6642             if (srcPath.size()>0)
6643                 {
6644                 srcName.append(srcPath);
6645                 srcName.append("/");
6646                 }
6647             srcName.append(dep.name);
6648             srcName.append(".");
6649             srcName.append(dep.suffix);
6650             String srcFullName = parent.resolve(srcName);
6651             bool compileMe = false;
6652             //# First we check if the source is newer than the .o
6653             if (isNewerThan(srcFullName, destFullName))
6654                 {
6655                 taskstatus("compile of %s required by source: %s",
6656                         destFullName.c_str(), srcFullName.c_str());
6657                 compileMe = true;
6658                 }
6659             else
6660                 {
6661                 //# secondly, we check if any of the included dependencies
6662                 //# of the .c/.cpp is newer than the .o
6663                 for (unsigned int i=0 ; i<dep.files.size() ; i++)
6664                     {
6665                     String depName;
6666                     if (source.size()>0)
6667                         {
6668                         depName.append(source);
6669                         depName.append("/");
6670                         }
6671                     depName.append(dep.files[i]);
6672                     String depFullName = parent.resolve(depName);
6673                     bool depRequires = isNewerThan(depFullName, destFullName);
6674                     //trace("%d %s %s\n", depRequires,
6675                     //        destFullName.c_str(), depFullName.c_str());
6676                     if (depRequires)
6677                         {
6678                         taskstatus("compile of %s required by included: %s",
6679                                 destFullName.c_str(), depFullName.c_str());
6680                         compileMe = true;
6681                         break;
6682                         }
6683                     }
6684                 }
6685             if (!compileMe)
6686                 {
6687                 continue;
6688                 }
6690             //## Assemble the command
6691             String cmd = command;
6692             cmd.append(" -c ");
6693             cmd.append(flags);
6694             cmd.append(" ");
6695             cmd.append(defines);
6696             cmd.append(" ");
6697             cmd.append(incs);
6698             cmd.append(" ");
6699             cmd.append(srcFullName);
6700             cmd.append(" -o ");
6701             cmd.append(destFullName);
6703             //## Execute the command
6705             String outString, errString;
6706             bool ret = executeCommand(cmd.c_str(), "", outString, errString);
6708             if (f)
6709                 {
6710                 fprintf(f, "########################### File : %s\n",
6711                              srcFullName.c_str());
6712                 fprintf(f, "#### COMMAND ###\n");
6713                 int col = 0;
6714                 for (unsigned int i = 0 ; i < cmd.size() ; i++)
6715                     {
6716                     char ch = cmd[i];
6717                     if (isspace(ch)  && col > 63)
6718                         {
6719                         fputc('\n', f);
6720                         col = 0;
6721                         }
6722                     else
6723                         {
6724                         fputc(ch, f);
6725                         col++;
6726                         }
6727                     if (col > 76)
6728                         {
6729                         fputc('\n', f);
6730                         col = 0;
6731                         }
6732                     }
6733                 fprintf(f, "\n");
6734                 fprintf(f, "#### STDOUT ###\n%s\n", outString.c_str());
6735                 fprintf(f, "#### STDERR ###\n%s\n\n", errString.c_str());
6736                 fflush(f);
6737                 }
6738             if (!ret)
6739                 {
6740                 error("problem compiling: %s", errString.c_str());
6741                 errorOccurred = true;
6742                 }
6743             if (errorOccurred && !continueOnError)
6744                 break;
6745             }
6747         if (f)
6748             {
6749             fclose(f);
6750             }
6751         
6752         return !errorOccurred;
6753         }
6756     virtual bool parse(Element *elem)
6757         {
6758         String s;
6759         if (!parent.getAttribute(elem, "command", commandOpt))
6760             return false;
6761         if (commandOpt.size()>0)
6762             { cxxCommandOpt = ccCommandOpt = commandOpt; }
6763         if (!parent.getAttribute(elem, "cc", ccCommandOpt))
6764             return false;
6765         if (!parent.getAttribute(elem, "cxx", cxxCommandOpt))
6766             return false;
6767         if (!parent.getAttribute(elem, "destdir", destOpt))
6768             return false;
6769         if (!parent.getAttribute(elem, "continueOnError", continueOnErrorOpt))
6770             return false;
6771         if (!parent.getAttribute(elem, "refreshCache", refreshCacheOpt))
6772             return false;
6774         std::vector<Element *> children = elem->getChildren();
6775         for (unsigned int i=0 ; i<children.size() ; i++)
6776             {
6777             Element *child = children[i];
6778             String tagName = child->getName();
6779             if (tagName == "flags")
6780                 {
6781                 if (!parent.getValue(child, flagsOpt))
6782                     return false;
6783                 flagsOpt = strip(flagsOpt);
6784                 }
6785             else if (tagName == "includes")
6786                 {
6787                 if (!parent.getValue(child, includesOpt))
6788                     return false;
6789                 includesOpt = strip(includesOpt);
6790                 }
6791             else if (tagName == "defines")
6792                 {
6793                 if (!parent.getValue(child, definesOpt))
6794                     return false;
6795                 definesOpt = strip(definesOpt);
6796                 }
6797             else if (tagName == "fileset")
6798                 {
6799                 if (!parseFileSet(child, parent, fileSet))
6800                     return false;
6801                 sourceOpt = fileSet.getDirectory();
6802                 }
6803             else if (tagName == "excludeinc")
6804                 {
6805                 if (!parseFileList(child, parent, excludeInc))
6806                     return false;
6807                 }
6808             }
6810         return true;
6811         }
6812         
6813 protected:
6815     String   commandOpt;
6816     String   ccCommandOpt;
6817     String   cxxCommandOpt;
6818     String   sourceOpt;
6819     String   destOpt;
6820     String   flagsOpt;
6821     String   definesOpt;
6822     String   includesOpt;
6823     String   continueOnErrorOpt;
6824     String   refreshCacheOpt;
6825     FileSet  fileSet;
6826     FileList excludeInc;
6827     
6828 };
6832 /**
6833  *
6834  */
6835 class TaskCopy : public Task
6837 public:
6839     typedef enum
6840         {
6841         CP_NONE,
6842         CP_TOFILE,
6843         CP_TODIR
6844         } CopyType;
6846     TaskCopy(MakeBase &par) : Task(par)
6847         {
6848         type        = TASK_COPY;
6849         name        = "copy";
6850         cptype      = CP_NONE;
6851         haveFileSet = false;
6852         }
6854     virtual ~TaskCopy()
6855         {}
6857     virtual bool execute()
6858         {
6859         String fileName   = parent.eval(fileNameOpt   , ".");
6860         String toFileName = parent.eval(toFileNameOpt , ".");
6861         String toDirName  = parent.eval(toDirNameOpt  , ".");
6862         bool   verbose    = parent.evalBool(verboseOpt, false);
6863         switch (cptype)
6864            {
6865            case CP_TOFILE:
6866                {
6867                if (fileName.size()>0)
6868                    {
6869                    taskstatus("%s to %s",
6870                         fileName.c_str(), toFileName.c_str());
6871                    String fullSource = parent.resolve(fileName);
6872                    String fullDest = parent.resolve(toFileName);
6873                    if (verbose)
6874                        taskstatus("copy %s to file %s", fullSource.c_str(),
6875                                           fullDest.c_str());
6876                    if (!isRegularFile(fullSource))
6877                        {
6878                        error("copy : file %s does not exist", fullSource.c_str());
6879                        return false;
6880                        }
6881                    if (!isNewerThan(fullSource, fullDest))
6882                        {
6883                        taskstatus("skipped");
6884                        return true;
6885                        }
6886                    if (!copyFile(fullSource, fullDest))
6887                        return false;
6888                    taskstatus("1 file copied");
6889                    }
6890                return true;
6891                }
6892            case CP_TODIR:
6893                {
6894                if (haveFileSet)
6895                    {
6896                    if (!listFiles(parent, fileSet))
6897                        return false;
6898                    String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
6900                    taskstatus("%s to %s",
6901                        fileSetDir.c_str(), toDirName.c_str());
6903                    int nrFiles = 0;
6904                    for (unsigned int i=0 ; i<fileSet.size() ; i++)
6905                        {
6906                        String fileName = fileSet[i];
6908                        String sourcePath;
6909                        if (fileSetDir.size()>0)
6910                            {
6911                            sourcePath.append(fileSetDir);
6912                            sourcePath.append("/");
6913                            }
6914                        sourcePath.append(fileName);
6915                        String fullSource = parent.resolve(sourcePath);
6916                        
6917                        //Get the immediate parent directory's base name
6918                        String baseFileSetDir = fileSetDir;
6919                        unsigned int pos = baseFileSetDir.find_last_of('/');
6920                        if (pos!=baseFileSetDir.npos &&
6921                                   pos < baseFileSetDir.size()-1)
6922                            baseFileSetDir =
6923                               baseFileSetDir.substr(pos+1,
6924                                    baseFileSetDir.size());
6925                        //Now make the new path
6926                        String destPath;
6927                        if (toDirName.size()>0)
6928                            {
6929                            destPath.append(toDirName);
6930                            destPath.append("/");
6931                            }
6932                        if (baseFileSetDir.size()>0)
6933                            {
6934                            destPath.append(baseFileSetDir);
6935                            destPath.append("/");
6936                            }
6937                        destPath.append(fileName);
6938                        String fullDest = parent.resolve(destPath);
6939                        //trace("fileName:%s", fileName.c_str());
6940                        if (verbose)
6941                            taskstatus("copy %s to new dir : %s",
6942                                  fullSource.c_str(), fullDest.c_str());
6943                        if (!isNewerThan(fullSource, fullDest))
6944                            {
6945                            if (verbose)
6946                                taskstatus("copy skipping %s", fullSource.c_str());
6947                            continue;
6948                            }
6949                        if (!copyFile(fullSource, fullDest))
6950                            return false;
6951                        nrFiles++;
6952                        }
6953                    taskstatus("%d file(s) copied", nrFiles);
6954                    }
6955                else //file source
6956                    {
6957                    //For file->dir we want only the basename of
6958                    //the source appended to the dest dir
6959                    taskstatus("%s to %s", 
6960                        fileName.c_str(), toDirName.c_str());
6961                    String baseName = fileName;
6962                    unsigned int pos = baseName.find_last_of('/');
6963                    if (pos!=baseName.npos && pos<baseName.size()-1)
6964                        baseName = baseName.substr(pos+1, baseName.size());
6965                    String fullSource = parent.resolve(fileName);
6966                    String destPath;
6967                    if (toDirName.size()>0)
6968                        {
6969                        destPath.append(toDirName);
6970                        destPath.append("/");
6971                        }
6972                    destPath.append(baseName);
6973                    String fullDest = parent.resolve(destPath);
6974                    if (verbose)
6975                        taskstatus("file %s to new dir : %s", fullSource.c_str(),
6976                                           fullDest.c_str());
6977                    if (!isRegularFile(fullSource))
6978                        {
6979                        error("copy : file %s does not exist", fullSource.c_str());
6980                        return false;
6981                        }
6982                    if (!isNewerThan(fullSource, fullDest))
6983                        {
6984                        taskstatus("skipped");
6985                        return true;
6986                        }
6987                    if (!copyFile(fullSource, fullDest))
6988                        return false;
6989                    taskstatus("1 file copied");
6990                    }
6991                return true;
6992                }
6993            }
6994         return true;
6995         }
6998     virtual bool parse(Element *elem)
6999         {
7000         if (!parent.getAttribute(elem, "file", fileNameOpt))
7001             return false;
7002         if (!parent.getAttribute(elem, "tofile", toFileNameOpt))
7003             return false;
7004         if (toFileNameOpt.size() > 0)
7005             cptype = CP_TOFILE;
7006         if (!parent.getAttribute(elem, "todir", toDirNameOpt))
7007             return false;
7008         if (toDirNameOpt.size() > 0)
7009             cptype = CP_TODIR;
7010         if (!parent.getAttribute(elem, "verbose", verboseOpt))
7011             return false;
7012             
7013         haveFileSet = false;
7014         
7015         std::vector<Element *> children = elem->getChildren();
7016         for (unsigned int i=0 ; i<children.size() ; i++)
7017             {
7018             Element *child = children[i];
7019             String tagName = child->getName();
7020             if (tagName == "fileset")
7021                 {
7022                 if (!parseFileSet(child, parent, fileSet))
7023                     {
7024                     error("problem getting fileset");
7025                     return false;
7026                     }
7027                 haveFileSet = true;
7028                 }
7029             }
7031         //Perform validity checks
7032         if (fileNameOpt.size()>0 && fileSet.size()>0)
7033             {
7034             error("<copy> can only have one of : file= and <fileset>");
7035             return false;
7036             }
7037         if (toFileNameOpt.size()>0 && toDirNameOpt.size()>0)
7038             {
7039             error("<copy> can only have one of : tofile= or todir=");
7040             return false;
7041             }
7042         if (haveFileSet && toDirNameOpt.size()==0)
7043             {
7044             error("a <copy> task with a <fileset> must have : todir=");
7045             return false;
7046             }
7047         if (cptype == CP_TOFILE && fileNameOpt.size()==0)
7048             {
7049             error("<copy> tofile= must be associated with : file=");
7050             return false;
7051             }
7052         if (cptype == CP_TODIR && fileNameOpt.size()==0 && !haveFileSet)
7053             {
7054             error("<copy> todir= must be associated with : file= or <fileset>");
7055             return false;
7056             }
7058         return true;
7059         }
7060         
7061 private:
7063     int cptype;
7064     bool haveFileSet;
7066     FileSet fileSet;
7067     String  fileNameOpt;
7068     String  toFileNameOpt;
7069     String  toDirNameOpt;
7070     String  verboseOpt;
7071 };
7074 /**
7075  *
7076  */
7077 class TaskDelete : public Task
7079 public:
7081     typedef enum
7082         {
7083         DEL_FILE,
7084         DEL_DIR,
7085         DEL_FILESET
7086         } DeleteType;
7088     TaskDelete(MakeBase &par) : Task(par)
7089         { 
7090         type        = TASK_DELETE;
7091         name        = "delete";
7092         delType     = DEL_FILE;
7093         }
7095     virtual ~TaskDelete()
7096         {}
7098     virtual bool execute()
7099         {
7100         String dirName   = parent.eval(dirNameOpt, ".");
7101         String fileName  = parent.eval(fileNameOpt, ".");
7102         bool verbose     = parent.evalBool(verboseOpt, false);
7103         bool quiet       = parent.evalBool(quietOpt, false);
7104         bool failOnError = parent.evalBool(failOnErrorOpt, true);
7105         struct stat finfo;
7106         switch (delType)
7107             {
7108             case DEL_FILE:
7109                 {
7110                 taskstatus("file: %s", fileName.c_str());
7111                 String fullName = parent.resolve(fileName);
7112                 char *fname = (char *)fullName.c_str();
7113                 if (!quiet && verbose)
7114                     taskstatus("path: %s", fname);
7115                 //does not exist
7116                 if (stat(fname, &finfo)<0)
7117                     {
7118                     if (failOnError)
7119                         return false;
7120                     else
7121                         return true;
7122                     }
7123                 //exists but is not a regular file
7124                 if (!S_ISREG(finfo.st_mode))
7125                     {
7126                     error("<delete> failed. '%s' exists and is not a regular file",
7127                           fname);
7128                     return false;
7129                     }
7130                 if (remove(fname)<0)
7131                     {
7132                     error("<delete> failed: %s", strerror(errno));
7133                     return false;
7134                     }
7135                 return true;
7136                 }
7137             case DEL_DIR:
7138                 {
7139                 taskstatus("dir: %s", dirName.c_str());
7140                 String fullDir = parent.resolve(dirName);
7141                 if (!quiet && verbose)
7142                     taskstatus("path: %s", fullDir.c_str());
7143                 if (!removeDirectory(fullDir))
7144                     return false;
7145                 return true;
7146                 }
7147             }
7148         return true;
7149         }
7151     virtual bool parse(Element *elem)
7152         {
7153         if (!parent.getAttribute(elem, "file", fileNameOpt))
7154             return false;
7155         if (fileNameOpt.size() > 0)
7156             delType = DEL_FILE;
7157         if (!parent.getAttribute(elem, "dir", dirNameOpt))
7158             return false;
7159         if (dirNameOpt.size() > 0)
7160             delType = DEL_DIR;
7161         if (fileNameOpt.size()>0 && dirNameOpt.size()>0)
7162             {
7163             error("<delete> can have one attribute of file= or dir=");
7164             return false;
7165             }
7166         if (fileNameOpt.size()==0 && dirNameOpt.size()==0)
7167             {
7168             error("<delete> must have one attribute of file= or dir=");
7169             return false;
7170             }
7171         if (!parent.getAttribute(elem, "verbose", verboseOpt))
7172             return false;
7173         if (!parent.getAttribute(elem, "quiet", quietOpt))
7174             return false;
7175         if (!parent.getAttribute(elem, "failonerror", failOnErrorOpt))
7176             return false;
7177         return true;
7178         }
7180 private:
7182     int delType;
7183     String dirNameOpt;
7184     String fileNameOpt;
7185     String verboseOpt;
7186     String quietOpt;
7187     String failOnErrorOpt;
7188 };
7191 /**
7192  * Send a message to stdout
7193  */
7194 class TaskEcho : public Task
7196 public:
7198     TaskEcho(MakeBase &par) : Task(par)
7199         { type = TASK_ECHO; name = "echo"; }
7201     virtual ~TaskEcho()
7202         {}
7204     virtual bool execute()
7205         {
7206         //let message have priority over text
7207         String message = parent.eval(messageOpt, "");
7208         String text    = parent.eval(textOpt, "");
7209         if (message.size() > 0)
7210             {
7211             fprintf(stdout, "%s\n", message.c_str());
7212             }
7213         else if (text.size() > 0)
7214             {
7215             fprintf(stdout, "%s\n", text.c_str());
7216             }
7217         return true;
7218         }
7220     virtual bool parse(Element *elem)
7221         {
7222         if (!parent.getValue(elem, textOpt))
7223             return false;
7224         textOpt    = leftJustify(textOpt);
7225         if (!parent.getAttribute(elem, "message", messageOpt))
7226             return false;
7227         return true;
7228         }
7230 private:
7232     String messageOpt;
7233     String textOpt;
7234 };
7238 /**
7239  *
7240  */
7241 class TaskJar : public Task
7243 public:
7245     TaskJar(MakeBase &par) : Task(par)
7246         { type = TASK_JAR; name = "jar"; }
7248     virtual ~TaskJar()
7249         {}
7251     virtual bool execute()
7252         {
7253         String command  = parent.eval(commandOpt, "jar");
7254         String basedir  = parent.eval(basedirOpt, ".");
7255         String destfile = parent.eval(destfileOpt, ".");
7257         String cmd = command;
7258         cmd.append(" -cf ");
7259         cmd.append(destfile);
7260         cmd.append(" -C ");
7261         cmd.append(basedir);
7262         cmd.append(" .");
7264         String execCmd = cmd;
7266         String outString, errString;
7267         bool ret = executeCommand(execCmd.c_str(), "", outString, errString);
7268         if (!ret)
7269             {
7270             error("<jar> command '%s' failed :\n %s",
7271                                       execCmd.c_str(), errString.c_str());
7272             return false;
7273             }
7274         return true;
7275         }
7277     virtual bool parse(Element *elem)
7278         {
7279         if (!parent.getAttribute(elem, "command", commandOpt))
7280             return false;
7281         if (!parent.getAttribute(elem, "basedir", basedirOpt))
7282             return false;
7283         if (!parent.getAttribute(elem, "destfile", destfileOpt))
7284             return false;
7285         if (basedirOpt.size() == 0 || destfileOpt.size() == 0)
7286             {
7287             error("<jar> required both basedir and destfile attributes to be set");
7288             return false;
7289             }
7290         return true;
7291         }
7293 private:
7295     String commandOpt;
7296     String basedirOpt;
7297     String destfileOpt;
7298 };
7301 /**
7302  *
7303  */
7304 class TaskJavac : public Task
7306 public:
7308     TaskJavac(MakeBase &par) : Task(par)
7309         { 
7310         type = TASK_JAVAC; name = "javac";
7311         }
7313     virtual ~TaskJavac()
7314         {}
7316     virtual bool execute()
7317         {
7318         String command  = parent.eval(commandOpt, "javac");
7319         String srcdir   = parent.eval(srcdirOpt, ".");
7320         String destdir  = parent.eval(destdirOpt, ".");
7321         String target   = parent.eval(targetOpt, "");
7323         std::vector<String> fileList;
7324         if (!listFiles(srcdir, "", fileList))
7325             {
7326             return false;
7327             }
7328         String cmd = command;
7329         cmd.append(" -d ");
7330         cmd.append(destdir);
7331         cmd.append(" -classpath ");
7332         cmd.append(destdir);
7333         cmd.append(" -sourcepath ");
7334         cmd.append(srcdir);
7335         cmd.append(" ");
7336         if (target.size()>0)
7337             {
7338             cmd.append(" -target ");
7339             cmd.append(target);
7340             cmd.append(" ");
7341             }
7342         String fname = "javalist.btool";
7343         FILE *f = fopen(fname.c_str(), "w");
7344         int count = 0;
7345         for (unsigned int i=0 ; i<fileList.size() ; i++)
7346             {
7347             String fname = fileList[i];
7348             String srcName = fname;
7349             if (fname.size()<6) //x.java
7350                 continue;
7351             if (fname.compare(fname.size()-5, 5, ".java") != 0)
7352                 continue;
7353             String baseName = fname.substr(0, fname.size()-5);
7354             String destName = baseName;
7355             destName.append(".class");
7357             String fullSrc = srcdir;
7358             fullSrc.append("/");
7359             fullSrc.append(fname);
7360             String fullDest = destdir;
7361             fullDest.append("/");
7362             fullDest.append(destName);
7363             //trace("fullsrc:%s fulldest:%s", fullSrc.c_str(), fullDest.c_str());
7364             if (!isNewerThan(fullSrc, fullDest))
7365                 continue;
7367             count++;
7368             fprintf(f, "%s\n", fullSrc.c_str());
7369             }
7370         fclose(f);
7371         if (!count)
7372             {
7373             taskstatus("nothing to do");
7374             return true;
7375             }
7377         taskstatus("compiling %d files", count);
7379         String execCmd = cmd;
7380         execCmd.append("@");
7381         execCmd.append(fname);
7383         String outString, errString;
7384         bool ret = executeCommand(execCmd.c_str(), "", outString, errString);
7385         if (!ret)
7386             {
7387             error("<javac> command '%s' failed :\n %s",
7388                                       execCmd.c_str(), errString.c_str());
7389             return false;
7390             }
7391         return true;
7392         }
7394     virtual bool parse(Element *elem)
7395         {
7396         if (!parent.getAttribute(elem, "command", commandOpt))
7397             return false;
7398         if (!parent.getAttribute(elem, "srcdir", srcdirOpt))
7399             return false;
7400         if (!parent.getAttribute(elem, "destdir", destdirOpt))
7401             return false;
7402         if (srcdirOpt.size() == 0 || destdirOpt.size() == 0)
7403             {
7404             error("<javac> required both srcdir and destdir attributes to be set");
7405             return false;
7406             }
7407         if (!parent.getAttribute(elem, "target", targetOpt))
7408             return false;
7409         return true;
7410         }
7412 private:
7414     String commandOpt;
7415     String srcdirOpt;
7416     String destdirOpt;
7417     String targetOpt;
7419 };
7422 /**
7423  *
7424  */
7425 class TaskLink : public Task
7427 public:
7429     TaskLink(MakeBase &par) : Task(par)
7430         {
7431         type = TASK_LINK; name = "link";
7432         }
7434     virtual ~TaskLink()
7435         {}
7437     virtual bool execute()
7438         {
7439         String  command        = parent.eval(commandOpt, "g++");
7440         String  fileName       = parent.eval(fileNameOpt, "");
7441         String  flags          = parent.eval(flagsOpt, "");
7442         String  libs           = parent.eval(libsOpt, "");
7443         bool    doStrip        = parent.evalBool(doStripOpt, false);
7444         String  symFileName    = parent.eval(symFileNameOpt, "");
7445         String  stripCommand   = parent.eval(stripCommandOpt, "strip");
7446         String  objcopyCommand = parent.eval(objcopyCommandOpt, "objcopy");
7448         if (!listFiles(parent, fileSet))
7449             return false;
7450         String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
7451         //trace("%d files in %s", fileSet.size(), fileSetDir.c_str());
7452         bool doit = false;
7453         String fullTarget = parent.resolve(fileName);
7454         String cmd = command;
7455         cmd.append(" -o ");
7456         cmd.append(fullTarget);
7457         cmd.append(" ");
7458         cmd.append(flags);
7459         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7460             {
7461             cmd.append(" ");
7462             String obj;
7463             if (fileSetDir.size()>0)
7464                 {
7465                 obj.append(fileSetDir);
7466                 obj.append("/");
7467                 }
7468             obj.append(fileSet[i]);
7469             String fullObj = parent.resolve(obj);
7470             String nativeFullObj = getNativePath(fullObj);
7471             cmd.append(nativeFullObj);
7472             //trace("link: tgt:%s obj:%s", fullTarget.c_str(),
7473             //          fullObj.c_str());
7474             if (isNewerThan(fullObj, fullTarget))
7475                 doit = true;
7476             }
7477         cmd.append(" ");
7478         cmd.append(libs);
7479         if (!doit)
7480             {
7481             //trace("link not needed");
7482             return true;
7483             }
7484         //trace("LINK cmd:%s", cmd.c_str());
7487         String outbuf, errbuf;
7488         if (!executeCommand(cmd.c_str(), "", outbuf, errbuf))
7489             {
7490             error("LINK problem: %s", errbuf.c_str());
7491             return false;
7492             }
7494         if (symFileName.size()>0)
7495             {
7496             String symFullName = parent.resolve(symFileName);
7497             cmd = objcopyCommand;
7498             cmd.append(" --only-keep-debug ");
7499             cmd.append(getNativePath(fullTarget));
7500             cmd.append(" ");
7501             cmd.append(getNativePath(symFullName));
7502             if (!executeCommand(cmd, "", outbuf, errbuf))
7503                 {
7504                 error("<strip> symbol file failed : %s", errbuf.c_str());
7505                 return false;
7506                 }
7507             }
7508             
7509         if (doStrip)
7510             {
7511             cmd = stripCommand;
7512             cmd.append(" ");
7513             cmd.append(getNativePath(fullTarget));
7514             if (!executeCommand(cmd, "", outbuf, errbuf))
7515                {
7516                error("<strip> failed : %s", errbuf.c_str());
7517                return false;
7518                }
7519             }
7521         return true;
7522         }
7524     virtual bool parse(Element *elem)
7525         {
7526         if (!parent.getAttribute(elem, "command", commandOpt))
7527             return false;
7528         if (!parent.getAttribute(elem, "objcopycommand", objcopyCommandOpt))
7529             return false;
7530         if (!parent.getAttribute(elem, "stripcommand", stripCommandOpt))
7531             return false;
7532         if (!parent.getAttribute(elem, "out", fileNameOpt))
7533             return false;
7534         if (!parent.getAttribute(elem, "strip", doStripOpt))
7535             return false;
7536         if (!parent.getAttribute(elem, "symfile", symFileNameOpt))
7537             return false;
7538             
7539         std::vector<Element *> children = elem->getChildren();
7540         for (unsigned int i=0 ; i<children.size() ; i++)
7541             {
7542             Element *child = children[i];
7543             String tagName = child->getName();
7544             if (tagName == "fileset")
7545                 {
7546                 if (!parseFileSet(child, parent, fileSet))
7547                     return false;
7548                 }
7549             else if (tagName == "flags")
7550                 {
7551                 if (!parent.getValue(child, flagsOpt))
7552                     return false;
7553                 flagsOpt = strip(flagsOpt);
7554                 }
7555             else if (tagName == "libs")
7556                 {
7557                 if (!parent.getValue(child, libsOpt))
7558                     return false;
7559                 libsOpt = strip(libsOpt);
7560                 }
7561             }
7562         return true;
7563         }
7565 private:
7567     FileSet fileSet;
7569     String  commandOpt;
7570     String  fileNameOpt;
7571     String  flagsOpt;
7572     String  libsOpt;
7573     String  doStripOpt;
7574     String  symFileNameOpt;
7575     String  stripCommandOpt;
7576     String  objcopyCommandOpt;
7578 };
7582 /**
7583  * Create a named file
7584  */
7585 class TaskMakeFile : public Task
7587 public:
7589     TaskMakeFile(MakeBase &par) : Task(par)
7590         { type = TASK_MAKEFILE; name = "makefile"; }
7592     virtual ~TaskMakeFile()
7593         {}
7595     virtual bool execute()
7596         {
7597         String fileName = parent.eval(fileNameOpt, "");
7598         String text     = parent.eval(textOpt, "");
7600         taskstatus("%s", fileName.c_str());
7601         String fullName = parent.resolve(fileName);
7602         if (!isNewerThan(parent.getURI().getPath(), fullName))
7603             {
7604             //trace("skipped <makefile>");
7605             return true;
7606             }
7607         String fullNative = getNativePath(fullName);
7608         //trace("fullName:%s", fullName.c_str());
7609         FILE *f = fopen(fullNative.c_str(), "w");
7610         if (!f)
7611             {
7612             error("<makefile> could not open %s for writing : %s",
7613                 fullName.c_str(), strerror(errno));
7614             return false;
7615             }
7616         for (unsigned int i=0 ; i<text.size() ; i++)
7617             fputc(text[i], f);
7618         fputc('\n', f);
7619         fclose(f);
7620         return true;
7621         }
7623     virtual bool parse(Element *elem)
7624         {
7625         if (!parent.getAttribute(elem, "file", fileNameOpt))
7626             return false;
7627         if (fileNameOpt.size() == 0)
7628             {
7629             error("<makefile> requires 'file=\"filename\"' attribute");
7630             return false;
7631             }
7632         if (!parent.getValue(elem, textOpt))
7633             return false;
7634         textOpt = leftJustify(textOpt);
7635         //trace("dirname:%s", dirName.c_str());
7636         return true;
7637         }
7639 private:
7641     String fileNameOpt;
7642     String textOpt;
7643 };
7647 /**
7648  * Create a named directory
7649  */
7650 class TaskMkDir : public Task
7652 public:
7654     TaskMkDir(MakeBase &par) : Task(par)
7655         { type = TASK_MKDIR; name = "mkdir"; }
7657     virtual ~TaskMkDir()
7658         {}
7660     virtual bool execute()
7661         {
7662         String dirName = parent.eval(dirNameOpt, ".");
7663         
7664         taskstatus("%s", dirName.c_str());
7665         String fullDir = parent.resolve(dirName);
7666         //trace("fullDir:%s", fullDir.c_str());
7667         if (!createDirectory(fullDir))
7668             return false;
7669         return true;
7670         }
7672     virtual bool parse(Element *elem)
7673         {
7674         if (!parent.getAttribute(elem, "dir", dirNameOpt))
7675             return false;
7676         if (dirNameOpt.size() == 0)
7677             {
7678             error("<mkdir> requires 'dir=\"dirname\"' attribute");
7679             return false;
7680             }
7681         return true;
7682         }
7684 private:
7686     String dirNameOpt;
7687 };
7691 /**
7692  * Create a named directory
7693  */
7694 class TaskMsgFmt: public Task
7696 public:
7698     TaskMsgFmt(MakeBase &par) : Task(par)
7699          { type = TASK_MSGFMT;  name = "msgfmt"; }
7701     virtual ~TaskMsgFmt()
7702         {}
7704     virtual bool execute()
7705         {
7706         String  command   = parent.eval(commandOpt, "msgfmt");
7707         String  toDirName = parent.eval(toDirNameOpt, ".");
7708         String  outName   = parent.eval(outNameOpt, "");
7709         bool    owndir    = parent.evalBool(owndirOpt, false);
7711         if (!listFiles(parent, fileSet))
7712             return false;
7713         String fileSetDir = fileSet.getDirectory();
7715         //trace("msgfmt: %d", fileSet.size());
7716         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7717             {
7718             String fileName = fileSet[i];
7719             if (getSuffix(fileName) != "po")
7720                 continue;
7721             String sourcePath;
7722             if (fileSetDir.size()>0)
7723                 {
7724                 sourcePath.append(fileSetDir);
7725                 sourcePath.append("/");
7726                 }
7727             sourcePath.append(fileName);
7728             String fullSource = parent.resolve(sourcePath);
7730             String destPath;
7731             if (toDirName.size()>0)
7732                 {
7733                 destPath.append(toDirName);
7734                 destPath.append("/");
7735                 }
7736             if (owndir)
7737                 {
7738                 String subdir = fileName;
7739                 unsigned int pos = subdir.find_last_of('.');
7740                 if (pos != subdir.npos)
7741                     subdir = subdir.substr(0, pos);
7742                 destPath.append(subdir);
7743                 destPath.append("/");
7744                 }
7745             //Pick the output file name
7746             if (outName.size() > 0)
7747                 {
7748                 destPath.append(outName);
7749                 }
7750             else
7751                 {
7752                 destPath.append(fileName);
7753                 destPath[destPath.size()-2] = 'm';
7754                 }
7756             String fullDest = parent.resolve(destPath);
7758             if (!isNewerThan(fullSource, fullDest))
7759                 {
7760                 //trace("skip %s", fullSource.c_str());
7761                 continue;
7762                 }
7763                 
7764             String cmd = command;
7765             cmd.append(" ");
7766             cmd.append(fullSource);
7767             cmd.append(" -o ");
7768             cmd.append(fullDest);
7769             
7770             int pos = fullDest.find_last_of('/');
7771             if (pos>0)
7772                 {
7773                 String fullDestPath = fullDest.substr(0, pos);
7774                 if (!createDirectory(fullDestPath))
7775                     return false;
7776                 }
7780             String outString, errString;
7781             if (!executeCommand(cmd.c_str(), "", outString, errString))
7782                 {
7783                 error("<msgfmt> problem: %s", errString.c_str());
7784                 return false;
7785                 }
7786             }
7788         return true;
7789         }
7791     virtual bool parse(Element *elem)
7792         {
7793         if (!parent.getAttribute(elem, "command", commandOpt))
7794             return false;
7795         if (!parent.getAttribute(elem, "todir", toDirNameOpt))
7796             return false;
7797         if (!parent.getAttribute(elem, "out", outNameOpt))
7798             return false;
7799         if (!parent.getAttribute(elem, "owndir", owndirOpt))
7800             return false;
7801             
7802         std::vector<Element *> children = elem->getChildren();
7803         for (unsigned int i=0 ; i<children.size() ; i++)
7804             {
7805             Element *child = children[i];
7806             String tagName = child->getName();
7807             if (tagName == "fileset")
7808                 {
7809                 if (!parseFileSet(child, parent, fileSet))
7810                     return false;
7811                 }
7812             }
7813         return true;
7814         }
7816 private:
7818     FileSet fileSet;
7820     String  commandOpt;
7821     String  toDirNameOpt;
7822     String  outNameOpt;
7823     String  owndirOpt;
7825 };
7829 /**
7830  *  Perform a Package-Config query similar to pkg-config
7831  */
7832 class TaskPkgConfig : public Task
7834 public:
7836     typedef enum
7837         {
7838         PKG_CONFIG_QUERY_CFLAGS,
7839         PKG_CONFIG_QUERY_LIBS,
7840         PKG_CONFIG_QUERY_ALL
7841         } QueryTypes;
7843     TaskPkgConfig(MakeBase &par) : Task(par)
7844         {
7845         type = TASK_PKG_CONFIG;
7846         name = "pkg-config";
7847         }
7849     virtual ~TaskPkgConfig()
7850         {}
7852     virtual bool execute()
7853         {
7854         String pkgName       = parent.eval(pkgNameOpt,      "");
7855         String prefix        = parent.eval(prefixOpt,       "");
7856         String propName      = parent.eval(propNameOpt,     "");
7857         String pkgConfigPath = parent.eval(pkgConfigPathOpt,"");
7858         String query         = parent.eval(queryOpt,        "all");
7860         String path = parent.resolve(pkgConfigPath);
7861         PkgConfig pkgconfig;
7862         pkgconfig.setPath(path);
7863         pkgconfig.setPrefix(prefix);
7864         if (!pkgconfig.query(pkgName))
7865             {
7866             error("<pkg-config> query failed for '%s", name.c_str());
7867             return false;
7868             }
7869             
7870         String val = "";
7871         if (query == "cflags")
7872             val = pkgconfig.getCflags();
7873         else if (query == "libs")
7874             val =pkgconfig.getLibs();
7875         else if (query == "all")
7876             val = pkgconfig.getAll();
7877         else
7878             {
7879             error("<pkg-config> unhandled query : %s", query.c_str());
7880             return false;
7881             }
7882         taskstatus("property %s = '%s'", propName.c_str(), val.c_str());
7883         parent.setProperty(propName, val);
7884         return true;
7885         }
7887     virtual bool parse(Element *elem)
7888         {
7889         //# NAME
7890         if (!parent.getAttribute(elem, "name", pkgNameOpt))
7891             return false;
7892         if (pkgNameOpt.size()==0)
7893             {
7894             error("<pkg-config> requires 'name=\"package\"' attribute");
7895             return false;
7896             }
7898         //# PROPERTY
7899         if (!parent.getAttribute(elem, "property", propNameOpt))
7900             return false;
7901         if (propNameOpt.size()==0)
7902             {
7903             error("<pkg-config> requires 'property=\"name\"' attribute");
7904             return false;
7905             }
7906         //# PATH
7907         if (!parent.getAttribute(elem, "path", pkgConfigPathOpt))
7908             return false;
7909         //# PREFIX
7910         if (!parent.getAttribute(elem, "prefix", prefixOpt))
7911             return false;
7912         //# QUERY
7913         if (!parent.getAttribute(elem, "query", queryOpt))
7914             return false;
7916         return true;
7917         }
7919 private:
7921     String queryOpt;
7922     String pkgNameOpt;
7923     String prefixOpt;
7924     String propNameOpt;
7925     String pkgConfigPathOpt;
7927 };
7934 /**
7935  *  Process an archive to allow random access
7936  */
7937 class TaskRanlib : public Task
7939 public:
7941     TaskRanlib(MakeBase &par) : Task(par)
7942         { type = TASK_RANLIB; name = "ranlib"; }
7944     virtual ~TaskRanlib()
7945         {}
7947     virtual bool execute()
7948         {
7949         String fileName = parent.eval(fileNameOpt, "");
7950         String command  = parent.eval(commandOpt, "ranlib");
7952         String fullName = parent.resolve(fileName);
7953         //trace("fullDir:%s", fullDir.c_str());
7954         String cmd = command;
7955         cmd.append(" ");
7956         cmd.append(fullName);
7957         String outbuf, errbuf;
7958         if (!executeCommand(cmd, "", outbuf, errbuf))
7959             return false;
7960         return true;
7961         }
7963     virtual bool parse(Element *elem)
7964         {
7965         if (!parent.getAttribute(elem, "command", commandOpt))
7966             return false;
7967         if (!parent.getAttribute(elem, "file", fileNameOpt))
7968             return false;
7969         if (fileNameOpt.size() == 0)
7970             {
7971             error("<ranlib> requires 'file=\"fileNname\"' attribute");
7972             return false;
7973             }
7974         return true;
7975         }
7977 private:
7979     String fileNameOpt;
7980     String commandOpt;
7981 };
7985 /**
7986  * Compile a resource file into a binary object
7987  */
7988 class TaskRC : public Task
7990 public:
7992     TaskRC(MakeBase &par) : Task(par)
7993         { type = TASK_RC; name = "rc"; }
7995     virtual ~TaskRC()
7996         {}
7998     virtual bool execute()
7999         {
8000         String command  = parent.eval(commandOpt,  "windres");
8001         String flags    = parent.eval(flagsOpt,    "");
8002         String fileName = parent.eval(fileNameOpt, "");
8003         String outName  = parent.eval(outNameOpt,  "");
8005         String fullFile = parent.resolve(fileName);
8006         String fullOut  = parent.resolve(outName);
8007         if (!isNewerThan(fullFile, fullOut))
8008             return true;
8009         String cmd = command;
8010         cmd.append(" -o ");
8011         cmd.append(fullOut);
8012         cmd.append(" ");
8013         cmd.append(flags);
8014         cmd.append(" ");
8015         cmd.append(fullFile);
8017         String outString, errString;
8018         if (!executeCommand(cmd.c_str(), "", outString, errString))
8019             {
8020             error("RC problem: %s", errString.c_str());
8021             return false;
8022             }
8023         return true;
8024         }
8026     virtual bool parse(Element *elem)
8027         {
8028         if (!parent.getAttribute(elem, "command", commandOpt))
8029             return false;
8030         if (!parent.getAttribute(elem, "file", fileNameOpt))
8031             return false;
8032         if (!parent.getAttribute(elem, "out", outNameOpt))
8033             return false;
8034         std::vector<Element *> children = elem->getChildren();
8035         for (unsigned int i=0 ; i<children.size() ; i++)
8036             {
8037             Element *child = children[i];
8038             String tagName = child->getName();
8039             if (tagName == "flags")
8040                 {
8041                 if (!parent.getValue(child, flagsOpt))
8042                     return false;
8043                 }
8044             }
8045         return true;
8046         }
8048 private:
8050     String commandOpt;
8051     String flagsOpt;
8052     String fileNameOpt;
8053     String outNameOpt;
8055 };
8059 /**
8060  *  Collect .o's into a .so or DLL
8061  */
8062 class TaskSharedLib : public Task
8064 public:
8066     TaskSharedLib(MakeBase &par) : Task(par)
8067         { type = TASK_SHAREDLIB; name = "dll"; }
8069     virtual ~TaskSharedLib()
8070         {}
8072     virtual bool execute()
8073         {
8074         String command     = parent.eval(commandOpt, "dllwrap");
8075         String fileName    = parent.eval(fileNameOpt, "");
8076         String defFileName = parent.eval(defFileNameOpt, "");
8077         String impFileName = parent.eval(impFileNameOpt, "");
8078         String libs        = parent.eval(libsOpt, "");
8080         //trace("###########HERE %d", fileSet.size());
8081         bool doit = false;
8082         
8083         String fullOut = parent.resolve(fileName);
8084         //trace("ar fullout: %s", fullOut.c_str());
8085         
8086         if (!listFiles(parent, fileSet))
8087             return false;
8088         String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
8090         for (unsigned int i=0 ; i<fileSet.size() ; i++)
8091             {
8092             String fname;
8093             if (fileSetDir.size()>0)
8094                 {
8095                 fname.append(fileSetDir);
8096                 fname.append("/");
8097                 }
8098             fname.append(fileSet[i]);
8099             String fullName = parent.resolve(fname);
8100             //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
8101             if (isNewerThan(fullName, fullOut))
8102                 doit = true;
8103             }
8104         //trace("Needs it:%d", doit);
8105         if (!doit)
8106             {
8107             return true;
8108             }
8110         String cmd = "dllwrap";
8111         cmd.append(" -o ");
8112         cmd.append(fullOut);
8113         if (defFileName.size()>0)
8114             {
8115             cmd.append(" --def ");
8116             cmd.append(defFileName);
8117             cmd.append(" ");
8118             }
8119         if (impFileName.size()>0)
8120             {
8121             cmd.append(" --implib ");
8122             cmd.append(impFileName);
8123             cmd.append(" ");
8124             }
8125         for (unsigned int i=0 ; i<fileSet.size() ; i++)
8126             {
8127             String fname;
8128             if (fileSetDir.size()>0)
8129                 {
8130                 fname.append(fileSetDir);
8131                 fname.append("/");
8132                 }
8133             fname.append(fileSet[i]);
8134             String fullName = parent.resolve(fname);
8136             cmd.append(" ");
8137             cmd.append(fullName);
8138             }
8139         cmd.append(" ");
8140         cmd.append(libs);
8142         String outString, errString;
8143         if (!executeCommand(cmd.c_str(), "", outString, errString))
8144             {
8145             error("<sharedlib> problem: %s", errString.c_str());
8146             return false;
8147             }
8149         return true;
8150         }
8152     virtual bool parse(Element *elem)
8153         {
8154         if (!parent.getAttribute(elem, "command", commandOpt))
8155             return false;
8156         if (!parent.getAttribute(elem, "file", fileNameOpt))
8157             return false;
8158         if (!parent.getAttribute(elem, "import", impFileNameOpt))
8159             return false;
8160         if (!parent.getAttribute(elem, "def", defFileNameOpt))
8161             return false;
8162             
8163         std::vector<Element *> children = elem->getChildren();
8164         for (unsigned int i=0 ; i<children.size() ; i++)
8165             {
8166             Element *child = children[i];
8167             String tagName = child->getName();
8168             if (tagName == "fileset")
8169                 {
8170                 if (!parseFileSet(child, parent, fileSet))
8171                     return false;
8172                 }
8173             else if (tagName == "libs")
8174                 {
8175                 if (!parent.getValue(child, libsOpt))
8176                     return false;
8177                 libsOpt = strip(libsOpt);
8178                 }
8179             }
8180         return true;
8181         }
8183 private:
8185     FileSet fileSet;
8187     String commandOpt;
8188     String fileNameOpt;
8189     String defFileNameOpt;
8190     String impFileNameOpt;
8191     String libsOpt;
8193 };
8197 /**
8198  * Run the "ar" command to archive .o's into a .a
8199  */
8200 class TaskStaticLib : public Task
8202 public:
8204     TaskStaticLib(MakeBase &par) : Task(par)
8205         { type = TASK_STATICLIB; name = "staticlib"; }
8207     virtual ~TaskStaticLib()
8208         {}
8210     virtual bool execute()
8211         {
8212         String command = parent.eval(commandOpt, "ar crv");
8213         String fileName = parent.eval(fileNameOpt, "");
8215         bool doit = false;
8216         
8217         String fullOut = parent.resolve(fileName);
8218         //trace("ar fullout: %s", fullOut.c_str());
8219         
8220         if (!listFiles(parent, fileSet))
8221             return false;
8222         String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
8223         //trace("###########HERE %s", fileSetDir.c_str());
8225         for (unsigned int i=0 ; i<fileSet.size() ; i++)
8226             {
8227             String fname;
8228             if (fileSetDir.size()>0)
8229                 {
8230                 fname.append(fileSetDir);
8231                 fname.append("/");
8232                 }
8233             fname.append(fileSet[i]);
8234             String fullName = parent.resolve(fname);
8235             //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
8236             if (isNewerThan(fullName, fullOut))
8237                 doit = true;
8238             }
8239         //trace("Needs it:%d", doit);
8240         if (!doit)
8241             {
8242             return true;
8243             }
8245         String cmd = command;
8246         cmd.append(" ");
8247         cmd.append(fullOut);
8248         for (unsigned int i=0 ; i<fileSet.size() ; i++)
8249             {
8250             String fname;
8251             if (fileSetDir.size()>0)
8252                 {
8253                 fname.append(fileSetDir);
8254                 fname.append("/");
8255                 }
8256             fname.append(fileSet[i]);
8257             String fullName = parent.resolve(fname);
8259             cmd.append(" ");
8260             cmd.append(fullName);
8261             }
8263         String outString, errString;
8264         if (!executeCommand(cmd.c_str(), "", outString, errString))
8265             {
8266             error("<staticlib> problem: %s", errString.c_str());
8267             return false;
8268             }
8270         return true;
8271         }
8274     virtual bool parse(Element *elem)
8275         {
8276         if (!parent.getAttribute(elem, "command", commandOpt))
8277             return false;
8278         if (!parent.getAttribute(elem, "file", fileNameOpt))
8279             return false;
8280             
8281         std::vector<Element *> children = elem->getChildren();
8282         for (unsigned int i=0 ; i<children.size() ; i++)
8283             {
8284             Element *child = children[i];
8285             String tagName = child->getName();
8286             if (tagName == "fileset")
8287                 {
8288                 if (!parseFileSet(child, parent, fileSet))
8289                     return false;
8290                 }
8291             }
8292         return true;
8293         }
8295 private:
8297     FileSet fileSet;
8299     String commandOpt;
8300     String fileNameOpt;
8302 };
8307 /**
8308  * Strip an executable
8309  */
8310 class TaskStrip : public Task
8312 public:
8314     TaskStrip(MakeBase &par) : Task(par)
8315         { type = TASK_STRIP; name = "strip"; }
8317     virtual ~TaskStrip()
8318         {}
8320     virtual bool execute()
8321         {
8322         String command     = parent.eval(commandOpt, "strip");
8323         String fileName    = parent.eval(fileNameOpt, "");
8324         String symFileName = parent.eval(symFileNameOpt, "");
8326         String fullName = parent.resolve(fileName);
8327         //trace("fullDir:%s", fullDir.c_str());
8328         String cmd;
8329         String outbuf, errbuf;
8331         if (symFileName.size()>0)
8332             {
8333             String symFullName = parent.resolve(symFileName);
8334             cmd = "objcopy --only-keep-debug ";
8335             cmd.append(getNativePath(fullName));
8336             cmd.append(" ");
8337             cmd.append(getNativePath(symFullName));
8338             if (!executeCommand(cmd, "", outbuf, errbuf))
8339                 {
8340                 error("<strip> symbol file failed : %s", errbuf.c_str());
8341                 return false;
8342                 }
8343             }
8344             
8345         cmd = command;
8346         cmd.append(getNativePath(fullName));
8347         if (!executeCommand(cmd, "", outbuf, errbuf))
8348             {
8349             error("<strip> failed : %s", errbuf.c_str());
8350             return false;
8351             }
8352         return true;
8353         }
8355     virtual bool parse(Element *elem)
8356         {
8357         if (!parent.getAttribute(elem, "command", commandOpt))
8358             return false;
8359         if (!parent.getAttribute(elem, "file", fileNameOpt))
8360             return false;
8361         if (!parent.getAttribute(elem, "symfile", symFileNameOpt))
8362             return false;
8363         if (fileNameOpt.size() == 0)
8364             {
8365             error("<strip> requires 'file=\"fileName\"' attribute");
8366             return false;
8367             }
8368         return true;
8369         }
8371 private:
8373     String commandOpt;
8374     String fileNameOpt;
8375     String symFileNameOpt;
8376 };
8379 /**
8380  *
8381  */
8382 class TaskTouch : public Task
8384 public:
8386     TaskTouch(MakeBase &par) : Task(par)
8387         { type = TASK_TOUCH; name = "touch"; }
8389     virtual ~TaskTouch()
8390         {}
8392     virtual bool execute()
8393         {
8394         String fileName = parent.eval(fileNameOpt, "");
8396         String fullName = parent.resolve(fileName);
8397         String nativeFile = getNativePath(fullName);
8398         if (!isRegularFile(fullName) && !isDirectory(fullName))
8399             {            
8400             // S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH
8401             int ret = creat(nativeFile.c_str(), 0666);
8402             if (ret != 0) 
8403                 {
8404                 error("<touch> could not create '%s' : %s",
8405                     nativeFile.c_str(), strerror(ret));
8406                 return false;
8407                 }
8408             return true;
8409             }
8410         int ret = utime(nativeFile.c_str(), (struct utimbuf *)0);
8411         if (ret != 0)
8412             {
8413             error("<touch> could not update the modification time for '%s' : %s",
8414                 nativeFile.c_str(), strerror(ret));
8415             return false;
8416             }
8417         return true;
8418         }
8420     virtual bool parse(Element *elem)
8421         {
8422         //trace("touch parse");
8423         if (!parent.getAttribute(elem, "file", fileNameOpt))
8424             return false;
8425         if (fileNameOpt.size() == 0)
8426             {
8427             error("<touch> requires 'file=\"fileName\"' attribute");
8428             return false;
8429             }
8430         return true;
8431         }
8433     String fileNameOpt;
8434 };
8437 /**
8438  *
8439  */
8440 class TaskTstamp : public Task
8442 public:
8444     TaskTstamp(MakeBase &par) : Task(par)
8445         { type = TASK_TSTAMP; name = "tstamp"; }
8447     virtual ~TaskTstamp()
8448         {}
8450     virtual bool execute()
8451         {
8452         return true;
8453         }
8455     virtual bool parse(Element *elem)
8456         {
8457         //trace("tstamp parse");
8458         return true;
8459         }
8460 };
8464 /**
8465  *
8466  */
8467 Task *Task::createTask(Element *elem, int lineNr)
8469     String tagName = elem->getName();
8470     //trace("task:%s", tagName.c_str());
8471     Task *task = NULL;
8472     if (tagName == "cc")
8473         task = new TaskCC(parent);
8474     else if (tagName == "copy")
8475         task = new TaskCopy(parent);
8476     else if (tagName == "delete")
8477         task = new TaskDelete(parent);
8478     else if (tagName == "echo")
8479         task = new TaskEcho(parent);
8480     else if (tagName == "jar")
8481         task = new TaskJar(parent);
8482     else if (tagName == "javac")
8483         task = new TaskJavac(parent);
8484     else if (tagName == "link")
8485         task = new TaskLink(parent);
8486     else if (tagName == "makefile")
8487         task = new TaskMakeFile(parent);
8488     else if (tagName == "mkdir")
8489         task = new TaskMkDir(parent);
8490     else if (tagName == "msgfmt")
8491         task = new TaskMsgFmt(parent);
8492     else if (tagName == "pkg-config")
8493         task = new TaskPkgConfig(parent);
8494     else if (tagName == "ranlib")
8495         task = new TaskRanlib(parent);
8496     else if (tagName == "rc")
8497         task = new TaskRC(parent);
8498     else if (tagName == "sharedlib")
8499         task = new TaskSharedLib(parent);
8500     else if (tagName == "staticlib")
8501         task = new TaskStaticLib(parent);
8502     else if (tagName == "strip")
8503         task = new TaskStrip(parent);
8504     else if (tagName == "touch")
8505         task = new TaskTouch(parent);
8506     else if (tagName == "tstamp")
8507         task = new TaskTstamp(parent);
8508     else
8509         {
8510         error("Unknown task '%s'", tagName.c_str());
8511         return NULL;
8512         }
8514     task->setLine(lineNr);
8516     if (!task->parse(elem))
8517         {
8518         delete task;
8519         return NULL;
8520         }
8521     return task;
8526 //########################################################################
8527 //# T A R G E T
8528 //########################################################################
8530 /**
8531  *
8532  */
8533 class Target : public MakeBase
8536 public:
8538     /**
8539      *
8540      */
8541     Target(Make &par) : parent(par)
8542         { init(); }
8544     /**
8545      *
8546      */
8547     Target(const Target &other) : parent(other.parent)
8548         { init(); assign(other); }
8550     /**
8551      *
8552      */
8553     Target &operator=(const Target &other)
8554         { init(); assign(other); return *this; }
8556     /**
8557      *
8558      */
8559     virtual ~Target()
8560         { cleanup() ; }
8563     /**
8564      *
8565      */
8566     virtual Make &getParent()
8567         { return parent; }
8569     /**
8570      *
8571      */
8572     virtual String getName()
8573         { return name; }
8575     /**
8576      *
8577      */
8578     virtual void setName(const String &val)
8579         { name = val; }
8581     /**
8582      *
8583      */
8584     virtual String getDescription()
8585         { return description; }
8587     /**
8588      *
8589      */
8590     virtual void setDescription(const String &val)
8591         { description = val; }
8593     /**
8594      *
8595      */
8596     virtual void addDependency(const String &val)
8597         { deps.push_back(val); }
8599     /**
8600      *
8601      */
8602     virtual void parseDependencies(const String &val)
8603         { deps = tokenize(val, ", "); }
8605     /**
8606      *
8607      */
8608     virtual std::vector<String> &getDependencies()
8609         { return deps; }
8611     /**
8612      *
8613      */
8614     virtual String getIf()
8615         { return ifVar; }
8617     /**
8618      *
8619      */
8620     virtual void setIf(const String &val)
8621         { ifVar = val; }
8623     /**
8624      *
8625      */
8626     virtual String getUnless()
8627         { return unlessVar; }
8629     /**
8630      *
8631      */
8632     virtual void setUnless(const String &val)
8633         { unlessVar = val; }
8635     /**
8636      *
8637      */
8638     virtual void addTask(Task *val)
8639         { tasks.push_back(val); }
8641     /**
8642      *
8643      */
8644     virtual std::vector<Task *> &getTasks()
8645         { return tasks; }
8647 private:
8649     void init()
8650         {
8651         }
8653     void cleanup()
8654         {
8655         tasks.clear();
8656         }
8658     void assign(const Target &other)
8659         {
8660         //parent      = other.parent;
8661         name        = other.name;
8662         description = other.description;
8663         ifVar       = other.ifVar;
8664         unlessVar   = other.unlessVar;
8665         deps        = other.deps;
8666         tasks       = other.tasks;
8667         }
8669     Make &parent;
8671     String name;
8673     String description;
8675     String ifVar;
8677     String unlessVar;
8679     std::vector<String> deps;
8681     std::vector<Task *> tasks;
8683 };
8692 //########################################################################
8693 //# M A K E
8694 //########################################################################
8697 /**
8698  *
8699  */
8700 class Make : public MakeBase
8703 public:
8705     /**
8706      *
8707      */
8708     Make()
8709         { init(); }
8711     /**
8712      *
8713      */
8714     Make(const Make &other)
8715         { assign(other); }
8717     /**
8718      *
8719      */
8720     Make &operator=(const Make &other)
8721         { assign(other); return *this; }
8723     /**
8724      *
8725      */
8726     virtual ~Make()
8727         { cleanup(); }
8729     /**
8730      *
8731      */
8732     virtual std::map<String, Target> &getTargets()
8733         { return targets; }
8736     /**
8737      *
8738      */
8739     virtual String version()
8740         { return BUILDTOOL_VERSION; }
8742     /**
8743      * Overload a <property>
8744      */
8745     virtual bool specifyProperty(const String &name,
8746                                  const String &value);
8748     /**
8749      *
8750      */
8751     virtual bool run();
8753     /**
8754      *
8755      */
8756     virtual bool run(const String &target);
8760 private:
8762     /**
8763      *
8764      */
8765     void init();
8767     /**
8768      *
8769      */
8770     void cleanup();
8772     /**
8773      *
8774      */
8775     void assign(const Make &other);
8777     /**
8778      *
8779      */
8780     bool executeTask(Task &task);
8783     /**
8784      *
8785      */
8786     bool executeTarget(Target &target,
8787              std::set<String> &targetsCompleted);
8790     /**
8791      *
8792      */
8793     bool execute();
8795     /**
8796      *
8797      */
8798     bool checkTargetDependencies(Target &prop,
8799                     std::vector<String> &depList);
8801     /**
8802      *
8803      */
8804     bool parsePropertyFile(const String &fileName,
8805                            const String &prefix);
8807     /**
8808      *
8809      */
8810     bool parseProperty(Element *elem);
8812     /**
8813      *
8814      */
8815     bool parseFile();
8817     /**
8818      *
8819      */
8820     std::vector<String> glob(const String &pattern);
8823     //###############
8824     //# Fields
8825     //###############
8827     String projectName;
8829     String currentTarget;
8831     String defaultTarget;
8833     String specifiedTarget;
8835     String baseDir;
8837     String description;
8838     
8839     //std::vector<Property> properties;
8840     
8841     std::map<String, Target> targets;
8843     std::vector<Task *> allTasks;
8844     
8845     std::map<String, String> specifiedProperties;
8847 };
8850 //########################################################################
8851 //# C L A S S  M A I N T E N A N C E
8852 //########################################################################
8854 /**
8855  *
8856  */
8857 void Make::init()
8859     uri             = "build.xml";
8860     projectName     = "";
8861     currentTarget   = "";
8862     defaultTarget   = "";
8863     specifiedTarget = "";
8864     baseDir         = "";
8865     description     = "";
8866     envPrefix       = "env.";
8867     pcPrefix        = "pc.";
8868     pccPrefix       = "pcc.";
8869     pclPrefix       = "pcl.";
8870     properties.clear();
8871     for (unsigned int i = 0 ; i < allTasks.size() ; i++)
8872         delete allTasks[i];
8873     allTasks.clear();
8878 /**
8879  *
8880  */
8881 void Make::cleanup()
8883     for (unsigned int i = 0 ; i < allTasks.size() ; i++)
8884         delete allTasks[i];
8885     allTasks.clear();
8890 /**
8891  *
8892  */
8893 void Make::assign(const Make &other)
8895     uri              = other.uri;
8896     projectName      = other.projectName;
8897     currentTarget    = other.currentTarget;
8898     defaultTarget    = other.defaultTarget;
8899     specifiedTarget  = other.specifiedTarget;
8900     baseDir          = other.baseDir;
8901     description      = other.description;
8902     properties       = other.properties;
8907 //########################################################################
8908 //# U T I L I T Y    T A S K S
8909 //########################################################################
8911 /**
8912  *  Perform a file globbing
8913  */
8914 std::vector<String> Make::glob(const String &pattern)
8916     std::vector<String> res;
8917     return res;
8921 //########################################################################
8922 //# P U B L I C    A P I
8923 //########################################################################
8927 /**
8928  *
8929  */
8930 bool Make::executeTarget(Target &target,
8931              std::set<String> &targetsCompleted)
8934     String name = target.getName();
8936     //First get any dependencies for this target
8937     std::vector<String> deps = target.getDependencies();
8938     for (unsigned int i=0 ; i<deps.size() ; i++)
8939         {
8940         String dep = deps[i];
8941         //Did we do it already?  Skip
8942         if (targetsCompleted.find(dep)!=targetsCompleted.end())
8943             continue;
8944             
8945         std::map<String, Target> &tgts =
8946                target.getParent().getTargets();
8947         std::map<String, Target>::iterator iter =
8948                tgts.find(dep);
8949         if (iter == tgts.end())
8950             {
8951             error("Target '%s' dependency '%s' not found",
8952                       name.c_str(),  dep.c_str());
8953             return false;
8954             }
8955         Target depTarget = iter->second;
8956         if (!executeTarget(depTarget, targetsCompleted))
8957             {
8958             return false;
8959             }
8960         }
8962     status("##### Target : %s\n##### %s", name.c_str(),
8963             target.getDescription().c_str());
8965     //Now let's do the tasks
8966     std::vector<Task *> &tasks = target.getTasks();
8967     for (unsigned int i=0 ; i<tasks.size() ; i++)
8968         {
8969         Task *task = tasks[i];
8970         status("--- %s / %s", name.c_str(), task->getName().c_str());
8971         if (!task->execute())
8972             {
8973             return false;
8974             }
8975         }
8976         
8977     targetsCompleted.insert(name);
8978     
8979     return true;
8984 /**
8985  *  Main execute() method.  Start here and work
8986  *  up the dependency tree 
8987  */
8988 bool Make::execute()
8990     status("######## EXECUTE");
8992     //Determine initial target
8993     if (specifiedTarget.size()>0)
8994         {
8995         currentTarget = specifiedTarget;
8996         }
8997     else if (defaultTarget.size()>0)
8998         {
8999         currentTarget = defaultTarget;
9000         }
9001     else
9002         {
9003         error("execute: no specified or default target requested");
9004         return false;
9005         }
9007     std::map<String, Target>::iterator iter =
9008                targets.find(currentTarget);
9009     if (iter == targets.end())
9010         {
9011         error("Initial target '%s' not found",
9012                  currentTarget.c_str());
9013         return false;
9014         }
9015         
9016     //Now run
9017     Target target = iter->second;
9018     std::set<String> targetsCompleted;
9019     if (!executeTarget(target, targetsCompleted))
9020         {
9021         return false;
9022         }
9024     status("######## EXECUTE COMPLETE");
9025     return true;
9031 /**
9032  *
9033  */
9034 bool Make::checkTargetDependencies(Target &target, 
9035                             std::vector<String> &depList)
9037     String tgtName = target.getName().c_str();
9038     depList.push_back(tgtName);
9040     std::vector<String> deps = target.getDependencies();
9041     for (unsigned int i=0 ; i<deps.size() ; i++)
9042         {
9043         String dep = deps[i];
9044         //First thing entered was the starting Target
9045         if (dep == depList[0])
9046             {
9047             error("Circular dependency '%s' found at '%s'",
9048                       dep.c_str(), tgtName.c_str());
9049             std::vector<String>::iterator diter;
9050             for (diter=depList.begin() ; diter!=depList.end() ; diter++)
9051                 {
9052                 error("  %s", diter->c_str());
9053                 }
9054             return false;
9055             }
9057         std::map<String, Target> &tgts =
9058                   target.getParent().getTargets();
9059         std::map<String, Target>::iterator titer = tgts.find(dep);
9060         if (titer == tgts.end())
9061             {
9062             error("Target '%s' dependency '%s' not found",
9063                       tgtName.c_str(), dep.c_str());
9064             return false;
9065             }
9066         if (!checkTargetDependencies(titer->second, depList))
9067             {
9068             return false;
9069             }
9070         }
9071     return true;
9078 static int getword(int pos, const String &inbuf, String &result)
9080     int p = pos;
9081     int len = (int)inbuf.size();
9082     String val;
9083     while (p < len)
9084         {
9085         char ch = inbuf[p];
9086         if (!isalnum(ch) && ch!='.' && ch!='_')
9087             break;
9088         val.push_back(ch);
9089         p++;
9090         }
9091     result = val;
9092     return p;
9098 /**
9099  *
9100  */
9101 bool Make::parsePropertyFile(const String &fileName,
9102                              const String &prefix)
9104     FILE *f = fopen(fileName.c_str(), "r");
9105     if (!f)
9106         {
9107         error("could not open property file %s", fileName.c_str());
9108         return false;
9109         }
9110     int linenr = 0;
9111     while (!feof(f))
9112         {
9113         char buf[256];
9114         if (!fgets(buf, 255, f))
9115             break;
9116         linenr++;
9117         String s = buf;
9118         s = trim(s);
9119         int len = s.size();
9120         if (len == 0)
9121             continue;
9122         if (s[0] == '#')
9123             continue;
9124         String key;
9125         String val;
9126         int p = 0;
9127         int p2 = getword(p, s, key);
9128         if (p2 <= p)
9129             {
9130             error("property file %s, line %d: expected keyword",
9131                     fileName.c_str(), linenr);
9132             return false;
9133             }
9134         if (prefix.size() > 0)
9135             {
9136             key.insert(0, prefix);
9137             }
9139         //skip whitespace
9140         for (p=p2 ; p<len ; p++)
9141             if (!isspace(s[p]))
9142                 break;
9144         if (p>=len || s[p]!='=')
9145             {
9146             error("property file %s, line %d: expected '='",
9147                     fileName.c_str(), linenr);
9148             return false;
9149             }
9150         p++;
9152         //skip whitespace
9153         for ( ; p<len ; p++)
9154             if (!isspace(s[p]))
9155                 break;
9157         /* This way expects a word after the =
9158         p2 = getword(p, s, val);
9159         if (p2 <= p)
9160             {
9161             error("property file %s, line %d: expected value",
9162                     fileName.c_str(), linenr);
9163             return false;
9164             }
9165         */
9166         // This way gets the rest of the line after the =
9167         if (p>=len)
9168             {
9169             error("property file %s, line %d: expected value",
9170                     fileName.c_str(), linenr);
9171             return false;
9172             }
9173         val = s.substr(p);
9174         if (key.size()==0)
9175             continue;
9176         //allow property to be set, even if val=""
9178         //trace("key:'%s' val:'%s'", key.c_str(), val.c_str());
9179         //See if we wanted to overload this property
9180         std::map<String, String>::iterator iter =
9181             specifiedProperties.find(key);
9182         if (iter!=specifiedProperties.end())
9183             {
9184             val = iter->second;
9185             status("overloading property '%s' = '%s'",
9186                    key.c_str(), val.c_str());
9187             }
9188         properties[key] = val;
9189         }
9190     fclose(f);
9191     return true;
9197 /**
9198  *
9199  */
9200 bool Make::parseProperty(Element *elem)
9202     std::vector<Attribute> &attrs = elem->getAttributes();
9203     for (unsigned int i=0 ; i<attrs.size() ; i++)
9204         {
9205         String attrName = attrs[i].getName();
9206         String attrVal  = attrs[i].getValue();
9208         if (attrName == "name")
9209             {
9210             String val;
9211             if (!getAttribute(elem, "value", val))
9212                 return false;
9213             if (val.size() > 0)
9214                 {
9215                 properties[attrVal] = val;
9216                 }
9217             else
9218                 {
9219                 if (!getAttribute(elem, "location", val))
9220                     return false;
9221                 //let the property exist, even if not defined
9222                 properties[attrVal] = val;
9223                 }
9224             //See if we wanted to overload this property
9225             std::map<String, String>::iterator iter =
9226                 specifiedProperties.find(attrVal);
9227             if (iter != specifiedProperties.end())
9228                 {
9229                 val = iter->second;
9230                 status("overloading property '%s' = '%s'",
9231                     attrVal.c_str(), val.c_str());
9232                 properties[attrVal] = val;
9233                 }
9234             }
9235         else if (attrName == "file")
9236             {
9237             String prefix;
9238             if (!getAttribute(elem, "prefix", prefix))
9239                 return false;
9240             if (prefix.size() > 0)
9241                 {
9242                 if (prefix[prefix.size()-1] != '.')
9243                     prefix.push_back('.');
9244                 }
9245             if (!parsePropertyFile(attrName, prefix))
9246                 return false;
9247             }
9248         else if (attrName == "environment")
9249             {
9250             if (attrVal.find('.') != attrVal.npos)
9251                 {
9252                 error("environment prefix cannot have a '.' in it");
9253                 return false;
9254                 }
9255             envPrefix = attrVal;
9256             envPrefix.push_back('.');
9257             }
9258         else if (attrName == "pkg-config")
9259             {
9260             if (attrVal.find('.') != attrVal.npos)
9261                 {
9262                 error("pkg-config prefix cannot have a '.' in it");
9263                 return false;
9264                 }
9265             pcPrefix = attrVal;
9266             pcPrefix.push_back('.');
9267             }
9268         else if (attrName == "pkg-config-cflags")
9269             {
9270             if (attrVal.find('.') != attrVal.npos)
9271                 {
9272                 error("pkg-config-cflags prefix cannot have a '.' in it");
9273                 return false;
9274                 }
9275             pccPrefix = attrVal;
9276             pccPrefix.push_back('.');
9277             }
9278         else if (attrName == "pkg-config-libs")
9279             {
9280             if (attrVal.find('.') != attrVal.npos)
9281                 {
9282                 error("pkg-config-libs prefix cannot have a '.' in it");
9283                 return false;
9284                 }
9285             pclPrefix = attrVal;
9286             pclPrefix.push_back('.');
9287             }
9288         }
9290     return true;
9296 /**
9297  *
9298  */
9299 bool Make::parseFile()
9301     status("######## PARSE : %s", uri.getPath().c_str());
9303     setLine(0);
9305     Parser parser;
9306     Element *root = parser.parseFile(uri.getNativePath());
9307     if (!root)
9308         {
9309         error("Could not open %s for reading",
9310               uri.getNativePath().c_str());
9311         return false;
9312         }
9313     
9314     setLine(root->getLine());
9316     if (root->getChildren().size()==0 ||
9317         root->getChildren()[0]->getName()!="project")
9318         {
9319         error("Main xml element should be <project>");
9320         delete root;
9321         return false;
9322         }
9324     //########## Project attributes
9325     Element *project = root->getChildren()[0];
9326     String s = project->getAttribute("name");
9327     if (s.size() > 0)
9328         projectName = s;
9329     s = project->getAttribute("default");
9330     if (s.size() > 0)
9331         defaultTarget = s;
9332     s = project->getAttribute("basedir");
9333     if (s.size() > 0)
9334         baseDir = s;
9336     //######### PARSE MEMBERS
9337     std::vector<Element *> children = project->getChildren();
9338     for (unsigned int i=0 ; i<children.size() ; i++)
9339         {
9340         Element *elem = children[i];
9341         setLine(elem->getLine());
9342         String tagName = elem->getName();
9344         //########## DESCRIPTION
9345         if (tagName == "description")
9346             {
9347             description = parser.trim(elem->getValue());
9348             }
9350         //######### PROPERTY
9351         else if (tagName == "property")
9352             {
9353             if (!parseProperty(elem))
9354                 return false;
9355             }
9357         //######### TARGET
9358         else if (tagName == "target")
9359             {
9360             String tname   = elem->getAttribute("name");
9361             String tdesc   = elem->getAttribute("description");
9362             String tdeps   = elem->getAttribute("depends");
9363             String tif     = elem->getAttribute("if");
9364             String tunless = elem->getAttribute("unless");
9365             Target target(*this);
9366             target.setName(tname);
9367             target.setDescription(tdesc);
9368             target.parseDependencies(tdeps);
9369             target.setIf(tif);
9370             target.setUnless(tunless);
9371             std::vector<Element *> telems = elem->getChildren();
9372             for (unsigned int i=0 ; i<telems.size() ; i++)
9373                 {
9374                 Element *telem = telems[i];
9375                 Task breeder(*this);
9376                 Task *task = breeder.createTask(telem, telem->getLine());
9377                 if (!task)
9378                     return false;
9379                 allTasks.push_back(task);
9380                 target.addTask(task);
9381                 }
9383             //Check name
9384             if (tname.size() == 0)
9385                 {
9386                 error("no name for target");
9387                 return false;
9388                 }
9389             //Check for duplicate name
9390             if (targets.find(tname) != targets.end())
9391                 {
9392                 error("target '%s' already defined", tname.c_str());
9393                 return false;
9394                 }
9395             //more work than targets[tname]=target, but avoids default allocator
9396             targets.insert(std::make_pair<String, Target>(tname, target));
9397             }
9398         //######### none of the above
9399         else
9400             {
9401             error("unknown toplevel tag: <%s>", tagName.c_str());
9402             return false;
9403             }
9405         }
9407     std::map<String, Target>::iterator iter;
9408     for (iter = targets.begin() ; iter!= targets.end() ; iter++)
9409         {
9410         Target tgt = iter->second;
9411         std::vector<String> depList;
9412         if (!checkTargetDependencies(tgt, depList))
9413             {
9414             return false;
9415             }
9416         }
9419     delete root;
9420     status("######## PARSE COMPLETE");
9421     return true;
9425 /**
9426  * Overload a <property>
9427  */
9428 bool Make::specifyProperty(const String &name, const String &value)
9430     if (specifiedProperties.find(name) != specifiedProperties.end())
9431         {
9432         error("Property %s already specified", name.c_str());
9433         return false;
9434         }
9435     specifiedProperties[name] = value;
9436     return true;
9441 /**
9442  *
9443  */
9444 bool Make::run()
9446     if (!parseFile())
9447         return false;
9448         
9449     if (!execute())
9450         return false;
9452     return true;
9458 /**
9459  * Get a formatted MM:SS.sss time elapsed string
9460  */ 
9461 static String
9462 timeDiffString(struct timeval &x, struct timeval &y)
9464     long microsX  = x.tv_usec;
9465     long secondsX = x.tv_sec;
9466     long microsY  = y.tv_usec;
9467     long secondsY = y.tv_sec;
9468     if (microsX < microsY)
9469         {
9470         microsX += 1000000;
9471         secondsX -= 1;
9472         }
9474     int seconds = (int)(secondsX - secondsY);
9475     int millis  = (int)((microsX - microsY)/1000);
9477     int minutes = seconds/60;
9478     seconds -= minutes*60;
9479     char buf[80];
9480     snprintf(buf, 79, "%dm %d.%03ds", minutes, seconds, millis);
9481     String ret = buf;
9482     return ret;
9483     
9486 /**
9487  *
9488  */
9489 bool Make::run(const String &target)
9491     status("####################################################");
9492     status("#   %s", version().c_str());
9493     status("####################################################");
9494     struct timeval timeStart, timeEnd;
9495     ::gettimeofday(&timeStart, NULL);
9496     specifiedTarget = target;
9497     if (!run())
9498         return false;
9499     ::gettimeofday(&timeEnd, NULL);
9500     String timeStr = timeDiffString(timeEnd, timeStart);
9501     status("####################################################");
9502     status("#   BuildTool Completed : %s", timeStr.c_str());
9503     status("####################################################");
9504     return true;
9513 }// namespace buildtool
9514 //########################################################################
9515 //# M A I N
9516 //########################################################################
9518 typedef buildtool::String String;
9520 /**
9521  *  Format an error message in printf() style
9522  */
9523 static void error(const char *fmt, ...)
9525     va_list ap;
9526     va_start(ap, fmt);
9527     fprintf(stderr, "BuildTool error: ");
9528     vfprintf(stderr, fmt, ap);
9529     fprintf(stderr, "\n");
9530     va_end(ap);
9534 static bool parseProperty(const String &s, String &name, String &val)
9536     int len = s.size();
9537     int i;
9538     for (i=0 ; i<len ; i++)
9539         {
9540         char ch = s[i];
9541         if (ch == '=')
9542             break;
9543         name.push_back(ch);
9544         }
9545     if (i>=len || s[i]!='=')
9546         {
9547         error("property requires -Dname=value");
9548         return false;
9549         }
9550     i++;
9551     for ( ; i<len ; i++)
9552         {
9553         char ch = s[i];
9554         val.push_back(ch);
9555         }
9556     return true;
9560 /**
9561  * Compare a buffer with a key, for the length of the key
9562  */
9563 static bool sequ(const String &buf, const char *key)
9565     int len = buf.size();
9566     for (int i=0 ; key[i] && i<len ; i++)
9567         {
9568         if (key[i] != buf[i])
9569             return false;
9570         }        
9571     return true;
9574 static void usage(int argc, char **argv)
9576     printf("usage:\n");
9577     printf("   %s [options] [target]\n", argv[0]);
9578     printf("Options:\n");
9579     printf("  -help, -h              print this message\n");
9580     printf("  -version               print the version information and exit\n");
9581     printf("  -file <file>           use given buildfile\n");
9582     printf("  -f <file>                 ''\n");
9583     printf("  -D<property>=<value>   use value for given property\n");
9589 /**
9590  * Parse the command-line args, get our options,
9591  * and run this thing
9592  */   
9593 static bool parseOptions(int argc, char **argv)
9595     if (argc < 1)
9596         {
9597         error("Cannot parse arguments");
9598         return false;
9599         }
9601     buildtool::Make make;
9603     String target;
9605     //char *progName = argv[0];
9606     for (int i=1 ; i<argc ; i++)
9607         {
9608         String arg = argv[i];
9609         if (arg.size()>1 && arg[0]=='-')
9610             {
9611             if (arg == "-h" || arg == "-help")
9612                 {
9613                 usage(argc,argv);
9614                 return true;
9615                 }
9616             else if (arg == "-version")
9617                 {
9618                 printf("%s", make.version().c_str());
9619                 return true;
9620                 }
9621             else if (arg == "-f" || arg == "-file")
9622                 {
9623                 if (i>=argc)
9624                    {
9625                    usage(argc, argv);
9626                    return false;
9627                    }
9628                 i++; //eat option
9629                 make.setURI(argv[i]);
9630                 }
9631             else if (arg.size()>2 && sequ(arg, "-D"))
9632                 {
9633                 String s = arg.substr(2, arg.size());
9634                 String name, value;
9635                 if (!parseProperty(s, name, value))
9636                    {
9637                    usage(argc, argv);
9638                    return false;
9639                    }
9640                 if (!make.specifyProperty(name, value))
9641                     return false;
9642                 }
9643             else
9644                 {
9645                 error("Unknown option:%s", arg.c_str());
9646                 return false;
9647                 }
9648             }
9649         else
9650             {
9651             if (target.size()>0)
9652                 {
9653                 error("only one initial target");
9654                 usage(argc, argv);
9655                 return false;
9656                 }
9657             target = arg;
9658             }
9659         }
9661     //We have the options.  Now execute them
9662     if (!make.run(target))
9663         return false;
9665     return true;
9671 /*
9672 static bool runMake()
9674     buildtool::Make make;
9675     if (!make.run())
9676         return false;
9677     return true;
9681 static bool pkgConfigTest()
9683     buildtool::PkgConfig pkgConfig;
9684     if (!pkgConfig.readFile("gtk+-2.0.pc"))
9685         return false;
9686     return true;
9691 static bool depTest()
9693     buildtool::DepTool deptool;
9694     deptool.setSourceDirectory("/dev/ink/inkscape/src");
9695     if (!deptool.generateDependencies("build.dep"))
9696         return false;
9697     std::vector<buildtool::FileRec> res =
9698            deptool.loadDepFile("build.dep");
9699     if (res.size() == 0)
9700         return false;
9701     return true;
9704 static bool popenTest()
9706     buildtool::Make make;
9707     buildtool::String out, err;
9708     bool ret = make.executeCommand("gcc xx.cpp", "", out, err);
9709     printf("Popen test:%d '%s' '%s'\n", ret, out.c_str(), err.c_str());
9710     return true;
9714 static bool propFileTest()
9716     buildtool::Make make;
9717     make.parsePropertyFile("test.prop", "test.");
9718     return true;
9720 */
9722 int main(int argc, char **argv)
9725     if (!parseOptions(argc, argv))
9726         return 1;
9727     /*
9728     if (!popenTest())
9729         return 1;
9731     if (!depTest())
9732         return 1;
9733     if (!propFileTest())
9734         return 1;
9735     if (runMake())
9736         return 1;
9737     */
9738     return 0;
9742 //########################################################################
9743 //# E N D 
9744 //########################################################################