Code

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