Code

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