Code

revert r19450. fixes buildtool.
[inkscape.git] / buildtool.cpp
1 /**
2  * Simple build automation tool.
3  *
4  * Authors:
5  *   Bob Jamison
6  *   Jasper van de Gronde
7  *
8  * Copyright (C) 2006-2008 Bob Jamison
9  *
10  *  This library is free software; you can redistribute it and/or
11  *  modify it under the terms of the GNU Lesser General Public
12  *  License as published by the Free Software Foundation; either
13  *  version 2.1 of the License, or (at your option) any later version.
14  *
15  *  This library is distributed in the hope that it will be useful,
16  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  *  Lesser General Public License for more details.
19  *
20  *  You should have received a copy of the GNU Lesser General Public
21  *  License along with this library; if not, write to the Free Software
22  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
23  */
25 /**
26  * To use this file, compile with:
27  * <pre>
28  * g++ -O3 buildtool.cpp -o btool.exe
29  * (or whatever your compiler might be)
30  * Then
31  * btool
32  * or
33  * btool {target}
34  *
35  * Note: if you are using MinGW, and a not very recent version of it,
36  * gettimeofday() might be missing.  If so, just build this file with
37  * this command:
38  * g++ -O3 -DNEED_GETTIMEOFDAY buildtool.cpp -o btool.exe
39  *
40  */
42 #define BUILDTOOL_VERSION  "BuildTool v0.9.5"
44 #include <stdio.h>
45 #include <fcntl.h>
46 #include <unistd.h>
47 #include <stdarg.h>
48 #include <sys/stat.h>
49 #include <time.h>
50 #include <sys/time.h>
51 #include <utime.h>
52 #include <dirent.h>
54 #include <string>
55 #include <map>
56 #include <set>
57 #include <vector>
58 #include <algorithm>
61 #ifdef __WIN32__
62 #include <windows.h>
63 #endif
66 #include <errno.h>
69 //########################################################################
70 //# Definition of gettimeofday() for those who don't have it
71 //########################################################################
72 #ifdef NEED_GETTIMEOFDAY
73 #include <sys/timeb.h>
75 struct timezone {
76       int tz_minuteswest; /* minutes west of Greenwich */
77       int tz_dsttime;     /* type of dst correction */
78     };
80 static int gettimeofday (struct timeval *tv, struct timezone *tz)
81 {
82    struct _timeb tb;
84    if (!tv)
85       return (-1);
87     _ftime (&tb);
88     tv->tv_sec  = tb.time;
89     tv->tv_usec = tb.millitm * 1000 + 500;
90     if (tz)
91         {
92         tz->tz_minuteswest = -60 * _timezone;
93         tz->tz_dsttime = _daylight;
94         }
95     return 0;
96 }
98 #endif
106 namespace buildtool
112 //########################################################################
113 //########################################################################
114 //##  R E G E X P
115 //########################################################################
116 //########################################################################
118 /**
119  * This is the T-Rex regular expression library, which we
120  * gratefully acknowledge.  It's clean code and small size allow
121  * us to embed it in BuildTool without adding a dependency
122  *
123  */    
125 //begin trex.h
127 #ifndef _TREX_H_
128 #define _TREX_H_
129 /***************************************************************
130     T-Rex a tiny regular expression library
132     Copyright (C) 2003-2006 Alberto Demichelis
134     This software is provided 'as-is', without any express 
135     or implied warranty. In no event will the authors be held 
136     liable for any damages arising from the use of this software.
138     Permission is granted to anyone to use this software for 
139     any purpose, including commercial applications, and to alter
140     it and redistribute it freely, subject to the following restrictions:
142         1. The origin of this software must not be misrepresented;
143         you must not claim that you wrote the original software.
144         If you use this software in a product, an acknowledgment
145         in the product documentation would be appreciated but
146         is not required.
148         2. Altered source versions must be plainly marked as such,
149         and must not be misrepresented as being the original software.
151         3. This notice may not be removed or altered from any
152         source distribution.
154 ****************************************************************/
156 #ifdef _UNICODE
157 #define TRexChar unsigned short
158 #define MAX_CHAR 0xFFFF
159 #define _TREXC(c) L##c 
160 #define trex_strlen wcslen
161 #define trex_printf wprintf
162 #else
163 #define TRexChar char
164 #define MAX_CHAR 0xFF
165 #define _TREXC(c) (c) 
166 #define trex_strlen strlen
167 #define trex_printf printf
168 #endif
170 #ifndef TREX_API
171 #define TREX_API extern
172 #endif
174 #define TRex_True 1
175 #define TRex_False 0
177 typedef unsigned int TRexBool;
178 typedef struct TRex TRex;
180 typedef struct {
181     const TRexChar *begin;
182     int len;
183 } TRexMatch;
185 TREX_API TRex *trex_compile(const TRexChar *pattern,const TRexChar **error);
186 TREX_API void trex_free(TRex *exp);
187 TREX_API TRexBool trex_match(TRex* exp,const TRexChar* text);
188 TREX_API TRexBool trex_search(TRex* exp,const TRexChar* text, const TRexChar** out_begin, const TRexChar** out_end);
189 TREX_API TRexBool trex_searchrange(TRex* exp,const TRexChar* text_begin,const TRexChar* text_end,const TRexChar** out_begin, const TRexChar** out_end);
190 TREX_API int trex_getsubexpcount(TRex* exp);
191 TREX_API TRexBool trex_getsubexp(TRex* exp, int n, TRexMatch *subexp);
193 #endif
195 //end trex.h
197 //start trex.c
200 #include <stdio.h>
201 #include <string>
203 /* see copyright notice in trex.h */
204 #include <string.h>
205 #include <stdlib.h>
206 #include <ctype.h>
207 #include <setjmp.h>
208 //#include "trex.h"
210 #ifdef _UNICODE
211 #define scisprint iswprint
212 #define scstrlen wcslen
213 #define scprintf wprintf
214 #define _SC(x) L(x)
215 #else
216 #define scisprint isprint
217 #define scstrlen strlen
218 #define scprintf printf
219 #define _SC(x) (x)
220 #endif
222 #ifdef _DEBUG
223 #include <stdio.h>
225 static const TRexChar *g_nnames[] =
227     _SC("NONE"),_SC("OP_GREEDY"),    _SC("OP_OR"),
228     _SC("OP_EXPR"),_SC("OP_NOCAPEXPR"),_SC("OP_DOT"),    _SC("OP_CLASS"),
229     _SC("OP_CCLASS"),_SC("OP_NCLASS"),_SC("OP_RANGE"),_SC("OP_CHAR"),
230     _SC("OP_EOL"),_SC("OP_BOL"),_SC("OP_WB")
231 };
233 #endif
234 #define OP_GREEDY        (MAX_CHAR+1) // * + ? {n}
235 #define OP_OR            (MAX_CHAR+2)
236 #define OP_EXPR            (MAX_CHAR+3) //parentesis ()
237 #define OP_NOCAPEXPR    (MAX_CHAR+4) //parentesis (?:)
238 #define OP_DOT            (MAX_CHAR+5)
239 #define OP_CLASS        (MAX_CHAR+6)
240 #define OP_CCLASS        (MAX_CHAR+7)
241 #define OP_NCLASS        (MAX_CHAR+8) //negates class the [^
242 #define OP_RANGE        (MAX_CHAR+9)
243 #define OP_CHAR            (MAX_CHAR+10)
244 #define OP_EOL            (MAX_CHAR+11)
245 #define OP_BOL            (MAX_CHAR+12)
246 #define OP_WB            (MAX_CHAR+13)
248 #define TREX_SYMBOL_ANY_CHAR ('.')
249 #define TREX_SYMBOL_GREEDY_ONE_OR_MORE ('+')
250 #define TREX_SYMBOL_GREEDY_ZERO_OR_MORE ('*')
251 #define TREX_SYMBOL_GREEDY_ZERO_OR_ONE ('?')
252 #define TREX_SYMBOL_BRANCH ('|')
253 #define TREX_SYMBOL_END_OF_STRING ('$')
254 #define TREX_SYMBOL_BEGINNING_OF_STRING ('^')
255 #define TREX_SYMBOL_ESCAPE_CHAR ('\\')
258 typedef int TRexNodeType;
260 typedef struct tagTRexNode{
261     TRexNodeType type;
262     int left;
263     int right;
264     int next;
265 }TRexNode;
267 struct TRex{
268     const TRexChar *_eol;
269     const TRexChar *_bol;
270     const TRexChar *_p;
271     int _first;
272     int _op;
273     TRexNode *_nodes;
274     int _nallocated;
275     int _nsize;
276     int _nsubexpr;
277     TRexMatch *_matches;
278     int _currsubexp;
279     void *_jmpbuf;
280     const TRexChar **_error;
281 };
283 static int trex_list(TRex *exp);
285 static int trex_newnode(TRex *exp, TRexNodeType type)
287     TRexNode n;
288     int newid;
289     n.type = type;
290     n.next = n.right = n.left = -1;
291     if(type == OP_EXPR)
292         n.right = exp->_nsubexpr++;
293     if(exp->_nallocated < (exp->_nsize + 1)) {
294         //int oldsize = exp->_nallocated;
295         exp->_nallocated *= 2;
296         exp->_nodes = (TRexNode *)realloc(exp->_nodes, exp->_nallocated * sizeof(TRexNode));
297     }
298     exp->_nodes[exp->_nsize++] = n;
299     newid = exp->_nsize - 1;
300     return (int)newid;
303 static void trex_error(TRex *exp,const TRexChar *error)
305     if(exp->_error) *exp->_error = error;
306     longjmp(*((jmp_buf*)exp->_jmpbuf),-1);
309 static void trex_expect(TRex *exp, int n){
310     if((*exp->_p) != n) 
311         trex_error(exp, _SC("expected paren"));
312     exp->_p++;
315 static TRexChar trex_escapechar(TRex *exp)
317     if(*exp->_p == TREX_SYMBOL_ESCAPE_CHAR){
318         exp->_p++;
319         switch(*exp->_p) {
320         case 'v': exp->_p++; return '\v';
321         case 'n': exp->_p++; return '\n';
322         case 't': exp->_p++; return '\t';
323         case 'r': exp->_p++; return '\r';
324         case 'f': exp->_p++; return '\f';
325         default: return (*exp->_p++);
326         }
327     } else if(!scisprint(*exp->_p)) trex_error(exp,_SC("letter expected"));
328     return (*exp->_p++);
331 static int trex_charclass(TRex *exp,int classid)
333     int n = trex_newnode(exp,OP_CCLASS);
334     exp->_nodes[n].left = classid;
335     return n;
338 static int trex_charnode(TRex *exp,TRexBool isclass)
340     TRexChar t;
341     if(*exp->_p == TREX_SYMBOL_ESCAPE_CHAR) {
342         exp->_p++;
343         switch(*exp->_p) {
344             case 'n': exp->_p++; return trex_newnode(exp,'\n');
345             case 't': exp->_p++; return trex_newnode(exp,'\t');
346             case 'r': exp->_p++; return trex_newnode(exp,'\r');
347             case 'f': exp->_p++; return trex_newnode(exp,'\f');
348             case 'v': exp->_p++; return trex_newnode(exp,'\v');
349             case 'a': case 'A': case 'w': case 'W': case 's': case 'S': 
350             case 'd': case 'D': case 'x': case 'X': case 'c': case 'C': 
351             case 'p': case 'P': case 'l': case 'u': 
352                 {
353                 t = *exp->_p; exp->_p++; 
354                 return trex_charclass(exp,t);
355                 }
356             case 'b': 
357             case 'B':
358                 if(!isclass) {
359                     int node = trex_newnode(exp,OP_WB);
360                     exp->_nodes[node].left = *exp->_p;
361                     exp->_p++; 
362                     return node;
363                 } //else default
364             default: 
365                 t = *exp->_p; exp->_p++; 
366                 return trex_newnode(exp,t);
367         }
368     }
369     else if(!scisprint(*exp->_p)) {
370         
371         trex_error(exp,_SC("letter expected"));
372     }
373     t = *exp->_p; exp->_p++; 
374     return trex_newnode(exp,t);
376 static int trex_class(TRex *exp)
378     int ret = -1;
379     int first = -1,chain;
380     if(*exp->_p == TREX_SYMBOL_BEGINNING_OF_STRING){
381         ret = trex_newnode(exp,OP_NCLASS);
382         exp->_p++;
383     }else ret = trex_newnode(exp,OP_CLASS);
384     
385     if(*exp->_p == ']') trex_error(exp,_SC("empty class"));
386     chain = ret;
387     while(*exp->_p != ']' && exp->_p != exp->_eol) {
388         if(*exp->_p == '-' && first != -1){ 
389             int r,t;
390             if(*exp->_p++ == ']') trex_error(exp,_SC("unfinished range"));
391             r = trex_newnode(exp,OP_RANGE);
392             if(first>*exp->_p) trex_error(exp,_SC("invalid range"));
393             if(exp->_nodes[first].type == OP_CCLASS) trex_error(exp,_SC("cannot use character classes in ranges"));
394             exp->_nodes[r].left = exp->_nodes[first].type;
395             t = trex_escapechar(exp);
396             exp->_nodes[r].right = t;
397             exp->_nodes[chain].next = r;
398             chain = r;
399             first = -1;
400         }
401         else{
402             if(first!=-1){
403                 int c = first;
404                 exp->_nodes[chain].next = c;
405                 chain = c;
406                 first = trex_charnode(exp,TRex_True);
407             }
408             else{
409                 first = trex_charnode(exp,TRex_True);
410             }
411         }
412     }
413     if(first!=-1){
414         int c = first;
415         exp->_nodes[chain].next = c;
416         chain = c;
417         first = -1;
418     }
419     /* hack? */
420     exp->_nodes[ret].left = exp->_nodes[ret].next;
421     exp->_nodes[ret].next = -1;
422     return ret;
425 static int trex_parsenumber(TRex *exp)
427     int ret = *exp->_p-'0';
428     int positions = 10;
429     exp->_p++;
430     while(isdigit(*exp->_p)) {
431         ret = ret*10+(*exp->_p++-'0');
432         if(positions==1000000000) trex_error(exp,_SC("overflow in numeric constant"));
433         positions *= 10;
434     };
435     return ret;
438 static int trex_element(TRex *exp)
440     int ret = -1;
441     switch(*exp->_p)
442     {
443     case '(': {
444         int expr,newn;
445         exp->_p++;
448         if(*exp->_p =='?') {
449             exp->_p++;
450             trex_expect(exp,':');
451             expr = trex_newnode(exp,OP_NOCAPEXPR);
452         }
453         else
454             expr = trex_newnode(exp,OP_EXPR);
455         newn = trex_list(exp);
456         exp->_nodes[expr].left = newn;
457         ret = expr;
458         trex_expect(exp,')');
459               }
460               break;
461     case '[':
462         exp->_p++;
463         ret = trex_class(exp);
464         trex_expect(exp,']');
465         break;
466     case TREX_SYMBOL_END_OF_STRING: exp->_p++; ret = trex_newnode(exp,OP_EOL);break;
467     case TREX_SYMBOL_ANY_CHAR: exp->_p++; ret = trex_newnode(exp,OP_DOT);break;
468     default:
469         ret = trex_charnode(exp,TRex_False);
470         break;
471     }
473     {
474         int op;
475         TRexBool isgreedy = TRex_False;
476         unsigned short p0 = 0, p1 = 0;
477         switch(*exp->_p){
478             case TREX_SYMBOL_GREEDY_ZERO_OR_MORE: p0 = 0; p1 = 0xFFFF; exp->_p++; isgreedy = TRex_True; break;
479             case TREX_SYMBOL_GREEDY_ONE_OR_MORE: p0 = 1; p1 = 0xFFFF; exp->_p++; isgreedy = TRex_True; break;
480             case TREX_SYMBOL_GREEDY_ZERO_OR_ONE: p0 = 0; p1 = 1; exp->_p++; isgreedy = TRex_True; break;
481             case '{':
482                 exp->_p++;
483                 if(!isdigit(*exp->_p)) trex_error(exp,_SC("number expected"));
484                 p0 = (unsigned short)trex_parsenumber(exp);
485                 /*******************************/
486                 switch(*exp->_p) {
487             case '}':
488                 p1 = p0; exp->_p++;
489                 break;
490             case ',':
491                 exp->_p++;
492                 p1 = 0xFFFF;
493                 if(isdigit(*exp->_p)){
494                     p1 = (unsigned short)trex_parsenumber(exp);
495                 }
496                 trex_expect(exp,'}');
497                 break;
498             default:
499                 trex_error(exp,_SC(", or } expected"));
500         }
501         /*******************************/
502         isgreedy = TRex_True; 
503         break;
505         }
506         if(isgreedy) {
507             int nnode = trex_newnode(exp,OP_GREEDY);
508             op = OP_GREEDY;
509             exp->_nodes[nnode].left = ret;
510             exp->_nodes[nnode].right = ((p0)<<16)|p1;
511             ret = nnode;
512         }
513     }
514     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')) {
515         int nnode = trex_element(exp);
516         exp->_nodes[ret].next = nnode;
517     }
519     return ret;
522 static int trex_list(TRex *exp)
524     int ret=-1,e;
525     if(*exp->_p == TREX_SYMBOL_BEGINNING_OF_STRING) {
526         exp->_p++;
527         ret = trex_newnode(exp,OP_BOL);
528     }
529     e = trex_element(exp);
530     if(ret != -1) {
531         exp->_nodes[ret].next = e;
532     }
533     else ret = e;
535     if(*exp->_p == TREX_SYMBOL_BRANCH) {
536         int temp,tright;
537         exp->_p++;
538         temp = trex_newnode(exp,OP_OR);
539         exp->_nodes[temp].left = ret;
540         tright = trex_list(exp);
541         exp->_nodes[temp].right = tright;
542         ret = temp;
543     }
544     return ret;
547 static TRexBool trex_matchcclass(int cclass,TRexChar c)
549     switch(cclass) {
550     case 'a': return isalpha(c)?TRex_True:TRex_False;
551     case 'A': return !isalpha(c)?TRex_True:TRex_False;
552     case 'w': return (isalnum(c) || c == '_')?TRex_True:TRex_False;
553     case 'W': return (!isalnum(c) && c != '_')?TRex_True:TRex_False;
554     case 's': return isspace(c)?TRex_True:TRex_False;
555     case 'S': return !isspace(c)?TRex_True:TRex_False;
556     case 'd': return isdigit(c)?TRex_True:TRex_False;
557     case 'D': return !isdigit(c)?TRex_True:TRex_False;
558     case 'x': return isxdigit(c)?TRex_True:TRex_False;
559     case 'X': return !isxdigit(c)?TRex_True:TRex_False;
560     case 'c': return iscntrl(c)?TRex_True:TRex_False;
561     case 'C': return !iscntrl(c)?TRex_True:TRex_False;
562     case 'p': return ispunct(c)?TRex_True:TRex_False;
563     case 'P': return !ispunct(c)?TRex_True:TRex_False;
564     case 'l': return islower(c)?TRex_True:TRex_False;
565     case 'u': return isupper(c)?TRex_True:TRex_False;
566     }
567     return TRex_False; /*cannot happen*/
570 static TRexBool trex_matchclass(TRex* exp,TRexNode *node,TRexChar c)
572     do {
573         switch(node->type) {
574             case OP_RANGE:
575                 if(c >= node->left && c <= node->right) return TRex_True;
576                 break;
577             case OP_CCLASS:
578                 if(trex_matchcclass(node->left,c)) return TRex_True;
579                 break;
580             default:
581                 if(c == node->type)return TRex_True;
582         }
583     } while((node->next != -1) && (node = &exp->_nodes[node->next]));
584     return TRex_False;
587 static const TRexChar *trex_matchnode(TRex* exp,TRexNode *node,const TRexChar *str,TRexNode *next)
589     
590     TRexNodeType type = node->type;
591     switch(type) {
592     case OP_GREEDY: {
593         //TRexNode *greedystop = (node->next != -1) ? &exp->_nodes[node->next] : NULL;
594         TRexNode *greedystop = NULL;
595         int p0 = (node->right >> 16)&0x0000FFFF, p1 = node->right&0x0000FFFF, nmaches = 0;
596         const TRexChar *s=str, *good = str;
598         if(node->next != -1) {
599             greedystop = &exp->_nodes[node->next];
600         }
601         else {
602             greedystop = next;
603         }
605         while((nmaches == 0xFFFF || nmaches < p1)) {
607             const TRexChar *stop;
608             if(!(s = trex_matchnode(exp,&exp->_nodes[node->left],s,greedystop)))
609                 break;
610             nmaches++;
611             good=s;
612             if(greedystop) {
613                 //checks that 0 matches satisfy the expression(if so skips)
614                 //if not would always stop(for instance if is a '?')
615                 if(greedystop->type != OP_GREEDY ||
616                 (greedystop->type == OP_GREEDY && ((greedystop->right >> 16)&0x0000FFFF) != 0))
617                 {
618                     TRexNode *gnext = NULL;
619                     if(greedystop->next != -1) {
620                         gnext = &exp->_nodes[greedystop->next];
621                     }else if(next && next->next != -1){
622                         gnext = &exp->_nodes[next->next];
623                     }
624                     stop = trex_matchnode(exp,greedystop,s,gnext);
625                     if(stop) {
626                         //if satisfied stop it
627                         if(p0 == p1 && p0 == nmaches) break;
628                         else if(nmaches >= p0 && p1 == 0xFFFF) break;
629                         else if(nmaches >= p0 && nmaches <= p1) break;
630                     }
631                 }
632             }
633             
634             if(s >= exp->_eol)
635                 break;
636         }
637         if(p0 == p1 && p0 == nmaches) return good;
638         else if(nmaches >= p0 && p1 == 0xFFFF) return good;
639         else if(nmaches >= p0 && nmaches <= p1) return good;
640         return NULL;
641     }
642     case OP_OR: {
643             const TRexChar *asd = str;
644             TRexNode *temp=&exp->_nodes[node->left];
645             while( (asd = trex_matchnode(exp,temp,asd,NULL)) ) {
646                 if(temp->next != -1)
647                     temp = &exp->_nodes[temp->next];
648                 else
649                     return asd;
650             }
651             asd = str;
652             temp = &exp->_nodes[node->right];
653             while( (asd = trex_matchnode(exp,temp,asd,NULL)) ) {
654                 if(temp->next != -1)
655                     temp = &exp->_nodes[temp->next];
656                 else
657                     return asd;
658             }
659             return NULL;
660             break;
661     }
662     case OP_EXPR:
663     case OP_NOCAPEXPR:{
664             TRexNode *n = &exp->_nodes[node->left];
665             const TRexChar *cur = str;
666             int capture = -1;
667             if(node->type != OP_NOCAPEXPR && node->right == exp->_currsubexp) {
668                 capture = exp->_currsubexp;
669                 exp->_matches[capture].begin = cur;
670                 exp->_currsubexp++;
671             }
672             
673             do {
674                 TRexNode *subnext = NULL;
675                 if(n->next != -1) {
676                     subnext = &exp->_nodes[n->next];
677                 }else {
678                     subnext = next;
679                 }
680                 if(!(cur = trex_matchnode(exp,n,cur,subnext))) {
681                     if(capture != -1){
682                         exp->_matches[capture].begin = 0;
683                         exp->_matches[capture].len = 0;
684                     }
685                     return NULL;
686                 }
687             } while((n->next != -1) && (n = &exp->_nodes[n->next]));
689             if(capture != -1) 
690                 exp->_matches[capture].len = cur - exp->_matches[capture].begin;
691             return cur;
692     }                 
693     case OP_WB:
694         if((str == exp->_bol && !isspace(*str))
695          || (str == exp->_eol && !isspace(*(str-1)))
696          || (!isspace(*str) && isspace(*(str+1)))
697          || (isspace(*str) && !isspace(*(str+1))) ) {
698             return (node->left == 'b')?str:NULL;
699         }
700         return (node->left == 'b')?NULL:str;
701     case OP_BOL:
702         if(str == exp->_bol) return str;
703         return NULL;
704     case OP_EOL:
705         if(str == exp->_eol) return str;
706         return NULL;
707     case OP_DOT:{
708         *str++;
709                 }
710         return str;
711     case OP_NCLASS:
712     case OP_CLASS:
713         if(trex_matchclass(exp,&exp->_nodes[node->left],*str)?(type == OP_CLASS?TRex_True:TRex_False):(type == OP_NCLASS?TRex_True:TRex_False)) {
714             *str++;
715             return str;
716         }
717         return NULL;
718     case OP_CCLASS:
719         if(trex_matchcclass(node->left,*str)) {
720             *str++;
721             return str;
722         }
723         return NULL;
724     default: /* char */
725         if(*str != node->type) return NULL;
726         *str++;
727         return str;
728     }
729     return NULL;
732 /* public api */
733 TRex *trex_compile(const TRexChar *pattern,const TRexChar **error)
735     TRex *exp = (TRex *)malloc(sizeof(TRex));
736     exp->_eol = exp->_bol = NULL;
737     exp->_p = pattern;
738     exp->_nallocated = (int)scstrlen(pattern) * sizeof(TRexChar);
739     exp->_nodes = (TRexNode *)malloc(exp->_nallocated * sizeof(TRexNode));
740     exp->_nsize = 0;
741     exp->_matches = 0;
742     exp->_nsubexpr = 0;
743     exp->_first = trex_newnode(exp,OP_EXPR);
744     exp->_error = error;
745     exp->_jmpbuf = malloc(sizeof(jmp_buf));
746     if(setjmp(*((jmp_buf*)exp->_jmpbuf)) == 0) {
747         int res = trex_list(exp);
748         exp->_nodes[exp->_first].left = res;
749         if(*exp->_p!='\0')
750             trex_error(exp,_SC("unexpected character"));
751 #ifdef _DEBUG
752         {
753             int nsize,i;
754             TRexNode *t;
755             nsize = exp->_nsize;
756             t = &exp->_nodes[0];
757             scprintf(_SC("\n"));
758             for(i = 0;i < nsize; i++) {
759                 if(exp->_nodes[i].type>MAX_CHAR)
760                     scprintf(_SC("[%02d] %10s "),i,g_nnames[exp->_nodes[i].type-MAX_CHAR]);
761                 else
762                     scprintf(_SC("[%02d] %10c "),i,exp->_nodes[i].type);
763                 scprintf(_SC("left %02d right %02d next %02d\n"),exp->_nodes[i].left,exp->_nodes[i].right,exp->_nodes[i].next);
764             }
765             scprintf(_SC("\n"));
766         }
767 #endif
768         exp->_matches = (TRexMatch *) malloc(exp->_nsubexpr * sizeof(TRexMatch));
769         memset(exp->_matches,0,exp->_nsubexpr * sizeof(TRexMatch));
770     }
771     else{
772         trex_free(exp);
773         return NULL;
774     }
775     return exp;
778 void trex_free(TRex *exp)
780     if(exp)    {
781         if(exp->_nodes) free(exp->_nodes);
782         if(exp->_jmpbuf) free(exp->_jmpbuf);
783         if(exp->_matches) free(exp->_matches);
784         free(exp);
785     }
788 TRexBool trex_match(TRex* exp,const TRexChar* text)
790     const TRexChar* res = NULL;
791     exp->_bol = text;
792     exp->_eol = text + scstrlen(text);
793     exp->_currsubexp = 0;
794     res = trex_matchnode(exp,exp->_nodes,text,NULL);
795     if(res == NULL || res != exp->_eol)
796         return TRex_False;
797     return TRex_True;
800 TRexBool trex_searchrange(TRex* exp,const TRexChar* text_begin,const TRexChar* text_end,const TRexChar** out_begin, const TRexChar** out_end)
802     const TRexChar *cur = NULL;
803     int node = exp->_first;
804     if(text_begin >= text_end) return TRex_False;
805     exp->_bol = text_begin;
806     exp->_eol = text_end;
807     do {
808         cur = text_begin;
809         while(node != -1) {
810             exp->_currsubexp = 0;
811             cur = trex_matchnode(exp,&exp->_nodes[node],cur,NULL);
812             if(!cur)
813                 break;
814             node = exp->_nodes[node].next;
815         }
816         *text_begin++;
817     } while(cur == NULL && text_begin != text_end);
819     if(cur == NULL)
820         return TRex_False;
822     --text_begin;
824     if(out_begin) *out_begin = text_begin;
825     if(out_end) *out_end = cur;
826     return TRex_True;
829 TRexBool trex_search(TRex* exp,const TRexChar* text, const TRexChar** out_begin, const TRexChar** out_end)
831     return trex_searchrange(exp,text,text + scstrlen(text),out_begin,out_end);
834 int trex_getsubexpcount(TRex* exp)
836     return exp->_nsubexpr;
839 TRexBool trex_getsubexp(TRex* exp, int n, TRexMatch *subexp)
841     if( n<0 || n >= exp->_nsubexpr) return TRex_False;
842     *subexp = exp->_matches[n];
843     return TRex_True;
847 //########################################################################
848 //########################################################################
849 //##  E N D    R E G E X P
850 //########################################################################
851 //########################################################################
857 //########################################################################
858 //########################################################################
859 //##  X M L
860 //########################################################################
861 //########################################################################
863 // Note:  This mini-dom library comes from Pedro, another little project
864 // of mine.
866 typedef std::string String;
867 typedef unsigned int XMLCh;
870 class Namespace
872 public:
873     Namespace()
874         {}
876     Namespace(const String &prefixArg, const String &namespaceURIArg)
877         {
878         prefix       = prefixArg;
879         namespaceURI = namespaceURIArg;
880         }
882     Namespace(const Namespace &other)
883         {
884         assign(other);
885         }
887     Namespace &operator=(const Namespace &other)
888         {
889         assign(other);
890         return *this;
891         }
893     virtual ~Namespace()
894         {}
896     virtual String getPrefix()
897         { return prefix; }
899     virtual String getNamespaceURI()
900         { return namespaceURI; }
902 protected:
904     void assign(const Namespace &other)
905         {
906         prefix       = other.prefix;
907         namespaceURI = other.namespaceURI;
908         }
910     String prefix;
911     String namespaceURI;
913 };
915 class Attribute
917 public:
918     Attribute()
919         {}
921     Attribute(const String &nameArg, const String &valueArg)
922         {
923         name  = nameArg;
924         value = valueArg;
925         }
927     Attribute(const Attribute &other)
928         {
929         assign(other);
930         }
932     Attribute &operator=(const Attribute &other)
933         {
934         assign(other);
935         return *this;
936         }
938     virtual ~Attribute()
939         {}
941     virtual String getName()
942         { return name; }
944     virtual String getValue()
945         { return value; }
947 protected:
949     void assign(const Attribute &other)
950         {
951         name  = other.name;
952         value = other.value;
953         }
955     String name;
956     String value;
958 };
961 class Element
963 friend class Parser;
965 public:
966     Element()
967         {
968         init();
969         }
971     Element(const String &nameArg)
972         {
973         init();
974         name   = nameArg;
975         }
977     Element(const String &nameArg, const String &valueArg)
978         {
979         init();
980         name   = nameArg;
981         value  = valueArg;
982         }
984     Element(const Element &other)
985         {
986         assign(other);
987         }
989     Element &operator=(const Element &other)
990         {
991         assign(other);
992         return *this;
993         }
995     virtual Element *clone();
997     virtual ~Element()
998         {
999         for (unsigned int i=0 ; i<children.size() ; i++)
1000             delete children[i];
1001         }
1003     virtual String getName()
1004         { return name; }
1006     virtual String getValue()
1007         { return value; }
1009     Element *getParent()
1010         { return parent; }
1012     std::vector<Element *> getChildren()
1013         { return children; }
1015     std::vector<Element *> findElements(const String &name);
1017     String getAttribute(const String &name);
1019     std::vector<Attribute> &getAttributes()
1020         { return attributes; } 
1022     String getTagAttribute(const String &tagName, const String &attrName);
1024     String getTagValue(const String &tagName);
1026     void addChild(Element *child);
1028     void addAttribute(const String &name, const String &value);
1030     void addNamespace(const String &prefix, const String &namespaceURI);
1033     /**
1034      * Prettyprint an XML tree to an output stream.  Elements are indented
1035      * according to element hierarchy.
1036      * @param f a stream to receive the output
1037      * @param elem the element to output
1038      */
1039     void writeIndented(FILE *f);
1041     /**
1042      * Prettyprint an XML tree to standard output.  This is the equivalent of
1043      * writeIndented(stdout).
1044      * @param elem the element to output
1045      */
1046     void print();
1047     
1048     int getLine()
1049         { return line; }
1051 protected:
1053     void init()
1054         {
1055         parent = NULL;
1056         line   = 0;
1057         }
1059     void assign(const Element &other)
1060         {
1061         parent     = other.parent;
1062         children   = other.children;
1063         attributes = other.attributes;
1064         namespaces = other.namespaces;
1065         name       = other.name;
1066         value      = other.value;
1067         line       = other.line;
1068         }
1070     void findElementsRecursive(std::vector<Element *>&res, const String &name);
1072     void writeIndentedRecursive(FILE *f, int indent);
1074     Element *parent;
1076     std::vector<Element *>children;
1078     std::vector<Attribute> attributes;
1079     std::vector<Namespace> namespaces;
1081     String name;
1082     String value;
1083     
1084     int line;
1085 };
1091 class Parser
1093 public:
1094     /**
1095      * Constructor
1096      */
1097     Parser()
1098         { init(); }
1100     virtual ~Parser()
1101         {}
1103     /**
1104      * Parse XML in a char buffer.
1105      * @param buf a character buffer to parse
1106      * @param pos position to start parsing
1107      * @param len number of chars, from pos, to parse.
1108      * @return a pointer to the root of the XML document;
1109      */
1110     Element *parse(const char *buf,int pos,int len);
1112     /**
1113      * Parse XML in a char buffer.
1114      * @param buf a character buffer to parse
1115      * @param pos position to start parsing
1116      * @param len number of chars, from pos, to parse.
1117      * @return a pointer to the root of the XML document;
1118      */
1119     Element *parse(const String &buf);
1121     /**
1122      * Parse a named XML file.  The file is loaded like a data file;
1123      * the original format is not preserved.
1124      * @param fileName the name of the file to read
1125      * @return a pointer to the root of the XML document;
1126      */
1127     Element *parseFile(const String &fileName);
1129     /**
1130      * Utility method to preprocess a string for XML
1131      * output, escaping its entities.
1132      * @param str the string to encode
1133      */
1134     static String encode(const String &str);
1136     /**
1137      *  Removes whitespace from beginning and end of a string
1138      */
1139     String trim(const String &s);
1141 private:
1143     void init()
1144         {
1145         keepGoing       = true;
1146         currentNode     = NULL;
1147         parselen        = 0;
1148         parsebuf        = NULL;
1149         currentPosition = 0;
1150         }
1152     int countLines(int begin, int end);
1154     void getLineAndColumn(int pos, int *lineNr, int *colNr);
1156     void error(const char *fmt, ...);
1158     int peek(int pos);
1160     int match(int pos, const char *text);
1162     int skipwhite(int p);
1164     int getWord(int p0, String &buf);
1166     int getQuoted(int p0, String &buf, int do_i_parse);
1168     int parseVersion(int p0);
1170     int parseDoctype(int p0);
1172     int parseElement(int p0, Element *par,int depth);
1174     Element *parse(XMLCh *buf,int pos,int len);
1176     bool       keepGoing;
1177     Element    *currentNode;
1178     int        parselen;
1179     XMLCh      *parsebuf;
1180     String     cdatabuf;
1181     int        currentPosition;
1182 };
1187 //########################################################################
1188 //# E L E M E N T
1189 //########################################################################
1191 Element *Element::clone()
1193     Element *elem = new Element(name, value);
1194     elem->parent     = parent;
1195     elem->attributes = attributes;
1196     elem->namespaces = namespaces;
1197     elem->line       = line;
1199     std::vector<Element *>::iterator iter;
1200     for (iter = children.begin(); iter != children.end() ; iter++)
1201         {
1202         elem->addChild((*iter)->clone());
1203         }
1204     return elem;
1208 void Element::findElementsRecursive(std::vector<Element *>&res, const String &name)
1210     if (getName() == name)
1211         {
1212         res.push_back(this);
1213         }
1214     for (unsigned int i=0; i<children.size() ; i++)
1215         children[i]->findElementsRecursive(res, name);
1218 std::vector<Element *> Element::findElements(const String &name)
1220     std::vector<Element *> res;
1221     findElementsRecursive(res, name);
1222     return res;
1225 String Element::getAttribute(const String &name)
1227     for (unsigned int i=0 ; i<attributes.size() ; i++)
1228         if (attributes[i].getName() ==name)
1229             return attributes[i].getValue();
1230     return "";
1233 String Element::getTagAttribute(const String &tagName, const String &attrName)
1235     std::vector<Element *>elems = findElements(tagName);
1236     if (elems.size() <1)
1237         return "";
1238     String res = elems[0]->getAttribute(attrName);
1239     return res;
1242 String Element::getTagValue(const String &tagName)
1244     std::vector<Element *>elems = findElements(tagName);
1245     if (elems.size() <1)
1246         return "";
1247     String res = elems[0]->getValue();
1248     return res;
1251 void Element::addChild(Element *child)
1253     if (!child)
1254         return;
1255     child->parent = this;
1256     children.push_back(child);
1260 void Element::addAttribute(const String &name, const String &value)
1262     Attribute attr(name, value);
1263     attributes.push_back(attr);
1266 void Element::addNamespace(const String &prefix, const String &namespaceURI)
1268     Namespace ns(prefix, namespaceURI);
1269     namespaces.push_back(ns);
1272 void Element::writeIndentedRecursive(FILE *f, int indent)
1274     int i;
1275     if (!f)
1276         return;
1277     //Opening tag, and attributes
1278     for (i=0;i<indent;i++)
1279         fputc(' ',f);
1280     fprintf(f,"<%s",name.c_str());
1281     for (unsigned int i=0 ; i<attributes.size() ; i++)
1282         {
1283         fprintf(f," %s=\"%s\"",
1284               attributes[i].getName().c_str(),
1285               attributes[i].getValue().c_str());
1286         }
1287     for (unsigned int i=0 ; i<namespaces.size() ; i++)
1288         {
1289         fprintf(f," xmlns:%s=\"%s\"",
1290               namespaces[i].getPrefix().c_str(),
1291               namespaces[i].getNamespaceURI().c_str());
1292         }
1293     fprintf(f,">\n");
1295     //Between the tags
1296     if (value.size() > 0)
1297         {
1298         for (int i=0;i<indent;i++)
1299             fputc(' ', f);
1300         fprintf(f," %s\n", value.c_str());
1301         }
1303     for (unsigned int i=0 ; i<children.size() ; i++)
1304         children[i]->writeIndentedRecursive(f, indent+2);
1306     //Closing tag
1307     for (int i=0; i<indent; i++)
1308         fputc(' ',f);
1309     fprintf(f,"</%s>\n", name.c_str());
1312 void Element::writeIndented(FILE *f)
1314     writeIndentedRecursive(f, 0);
1317 void Element::print()
1319     writeIndented(stdout);
1323 //########################################################################
1324 //# P A R S E R
1325 //########################################################################
1329 typedef struct
1330     {
1331     const char *escaped;
1332     char value;
1333     } EntityEntry;
1335 static EntityEntry entities[] =
1337     { "&amp;" , '&'  },
1338     { "&lt;"  , '<'  },
1339     { "&gt;"  , '>'  },
1340     { "&apos;", '\'' },
1341     { "&quot;", '"'  },
1342     { NULL    , '\0' }
1343 };
1347 /**
1348  *  Removes whitespace from beginning and end of a string
1349  */
1350 String Parser::trim(const String &s)
1352     if (s.size() < 1)
1353         return s;
1354     
1355     //Find first non-ws char
1356     unsigned int begin = 0;
1357     for ( ; begin < s.size() ; begin++)
1358         {
1359         if (!isspace(s[begin]))
1360             break;
1361         }
1363     //Find first non-ws char, going in reverse
1364     unsigned int end = s.size() - 1;
1365     for ( ; end > begin ; end--)
1366         {
1367         if (!isspace(s[end]))
1368             break;
1369         }
1370     //trace("begin:%d  end:%d", begin, end);
1372     String res = s.substr(begin, end-begin+1);
1373     return res;
1377 int Parser::countLines(int begin, int end)
1379     int count = 0;
1380     for (int i=begin ; i<end ; i++)
1381         {
1382         XMLCh ch = parsebuf[i];
1383         if (ch == '\n' || ch == '\r')
1384             count++;
1385         }
1386     return count;
1390 void Parser::getLineAndColumn(int pos, int *lineNr, int *colNr)
1392     int line = 1;
1393     int col  = 1;
1394     for (long i=0 ; i<pos ; i++)
1395         {
1396         XMLCh ch = parsebuf[i];
1397         if (ch == '\n' || ch == '\r')
1398             {
1399             col = 0;
1400             line ++;
1401             }
1402         else
1403             col++;
1404         }
1405     *lineNr = line;
1406     *colNr  = col;
1411 void Parser::error(const char *fmt, ...)
1413     int lineNr;
1414     int colNr;
1415     getLineAndColumn(currentPosition, &lineNr, &colNr);
1416     va_list args;
1417     fprintf(stderr, "xml error at line %d, column %d:", lineNr, colNr);
1418     va_start(args,fmt);
1419     vfprintf(stderr,fmt,args);
1420     va_end(args) ;
1421     fprintf(stderr, "\n");
1426 int Parser::peek(int pos)
1428     if (pos >= parselen)
1429         return -1;
1430     currentPosition = pos;
1431     int ch = parsebuf[pos];
1432     //printf("ch:%c\n", ch);
1433     return ch;
1438 String Parser::encode(const String &str)
1440     String ret;
1441     for (unsigned int i=0 ; i<str.size() ; i++)
1442         {
1443         XMLCh ch = (XMLCh)str[i];
1444         if (ch == '&')
1445             ret.append("&amp;");
1446         else if (ch == '<')
1447             ret.append("&lt;");
1448         else if (ch == '>')
1449             ret.append("&gt;");
1450         else if (ch == '\'')
1451             ret.append("&apos;");
1452         else if (ch == '"')
1453             ret.append("&quot;");
1454         else
1455             ret.push_back(ch);
1457         }
1458     return ret;
1462 int Parser::match(int p0, const char *text)
1464     int p = p0;
1465     while (*text)
1466         {
1467         if (peek(p) != *text)
1468             return p0;
1469         p++; text++;
1470         }
1471     return p;
1476 int Parser::skipwhite(int p)
1479     while (p<parselen)
1480         {
1481         int p2 = match(p, "<!--");
1482         if (p2 > p)
1483             {
1484             p = p2;
1485             while (p<parselen)
1486               {
1487               p2 = match(p, "-->");
1488               if (p2 > p)
1489                   {
1490                   p = p2;
1491                   break;
1492                   }
1493               p++;
1494               }
1495           }
1496       XMLCh b = peek(p);
1497       if (!isspace(b))
1498           break;
1499       p++;
1500       }
1501   return p;
1504 /* modify this to allow all chars for an element or attribute name*/
1505 int Parser::getWord(int p0, String &buf)
1507     int p = p0;
1508     while (p<parselen)
1509         {
1510         XMLCh b = peek(p);
1511         if (b<=' ' || b=='/' || b=='>' || b=='=')
1512             break;
1513         buf.push_back(b);
1514         p++;
1515         }
1516     return p;
1519 int Parser::getQuoted(int p0, String &buf, int do_i_parse)
1522     int p = p0;
1523     if (peek(p) != '"' && peek(p) != '\'')
1524         return p0;
1525     p++;
1527     while ( p<parselen )
1528         {
1529         XMLCh b = peek(p);
1530         if (b=='"' || b=='\'')
1531             break;
1532         if (b=='&' && do_i_parse)
1533             {
1534             bool found = false;
1535             for (EntityEntry *ee = entities ; ee->value ; ee++)
1536                 {
1537                 int p2 = match(p, ee->escaped);
1538                 if (p2>p)
1539                     {
1540                     buf.push_back(ee->value);
1541                     p = p2;
1542                     found = true;
1543                     break;
1544                     }
1545                 }
1546             if (!found)
1547                 {
1548                 error("unterminated entity");
1549                 return false;
1550                 }
1551             }
1552         else
1553             {
1554             buf.push_back(b);
1555             p++;
1556             }
1557         }
1558     return p;
1561 int Parser::parseVersion(int p0)
1563     //printf("### parseVersion: %d\n", p0);
1565     int p = p0;
1567     p = skipwhite(p0);
1569     if (peek(p) != '<')
1570         return p0;
1572     p++;
1573     if (p>=parselen || peek(p)!='?')
1574         return p0;
1576     p++;
1578     String buf;
1580     while (p<parselen)
1581         {
1582         XMLCh ch = peek(p);
1583         if (ch=='?')
1584             {
1585             p++;
1586             break;
1587             }
1588         buf.push_back(ch);
1589         p++;
1590         }
1592     if (peek(p) != '>')
1593         return p0;
1594     p++;
1596     //printf("Got version:%s\n",buf.c_str());
1597     return p;
1600 int Parser::parseDoctype(int p0)
1602     //printf("### parseDoctype: %d\n", p0);
1604     int p = p0;
1605     p = skipwhite(p);
1607     if (p>=parselen || peek(p)!='<')
1608         return p0;
1610     p++;
1612     if (peek(p)!='!' || peek(p+1)=='-')
1613         return p0;
1614     p++;
1616     String buf;
1617     while (p<parselen)
1618         {
1619         XMLCh ch = peek(p);
1620         if (ch=='>')
1621             {
1622             p++;
1623             break;
1624             }
1625         buf.push_back(ch);
1626         p++;
1627         }
1629     //printf("Got doctype:%s\n",buf.c_str());
1630     return p;
1635 int Parser::parseElement(int p0, Element *par,int lineNr)
1638     int p = p0;
1640     int p2 = p;
1642     p = skipwhite(p);
1644     //## Get open tag
1645     XMLCh ch = peek(p);
1646     if (ch!='<')
1647         return p0;
1649     //int line, col;
1650     //getLineAndColumn(p, &line, &col);
1652     p++;
1654     String openTagName;
1655     p = skipwhite(p);
1656     p = getWord(p, openTagName);
1657     //printf("####tag :%s\n", openTagName.c_str());
1658     p = skipwhite(p);
1660     //Add element to tree
1661     Element *n = new Element(openTagName);
1662     n->line = lineNr + countLines(p0, p);
1663     n->parent = par;
1664     par->addChild(n);
1666     // Get attributes
1667     if (peek(p) != '>')
1668         {
1669         while (p<parselen)
1670             {
1671             p = skipwhite(p);
1672             ch = peek(p);
1673             //printf("ch:%c\n",ch);
1674             if (ch=='>')
1675                 break;
1676             else if (ch=='/' && p<parselen+1)
1677                 {
1678                 p++;
1679                 p = skipwhite(p);
1680                 ch = peek(p);
1681                 if (ch=='>')
1682                     {
1683                     p++;
1684                     //printf("quick close\n");
1685                     return p;
1686                     }
1687                 }
1688             String attrName;
1689             p2 = getWord(p, attrName);
1690             if (p2==p)
1691                 break;
1692             //printf("name:%s",buf);
1693             p=p2;
1694             p = skipwhite(p);
1695             ch = peek(p);
1696             //printf("ch:%c\n",ch);
1697             if (ch!='=')
1698                 break;
1699             p++;
1700             p = skipwhite(p);
1701             // ch = parsebuf[p];
1702             // printf("ch:%c\n",ch);
1703             String attrVal;
1704             p2 = getQuoted(p, attrVal, true);
1705             p=p2+1;
1706             //printf("name:'%s'   value:'%s'\n",attrName.c_str(),attrVal.c_str());
1707             char *namestr = (char *)attrName.c_str();
1708             if (strncmp(namestr, "xmlns:", 6)==0)
1709                 n->addNamespace(attrName, attrVal);
1710             else
1711                 n->addAttribute(attrName, attrVal);
1712             }
1713         }
1715     bool cdata = false;
1717     p++;
1718     // ### Get intervening data ### */
1719     String data;
1720     while (p<parselen)
1721         {
1722         //# COMMENT
1723         p2 = match(p, "<!--");
1724         if (!cdata && p2>p)
1725             {
1726             p = p2;
1727             while (p<parselen)
1728                 {
1729                 p2 = match(p, "-->");
1730                 if (p2 > p)
1731                     {
1732                     p = p2;
1733                     break;
1734                     }
1735                 p++;
1736                 }
1737             }
1739         ch = peek(p);
1740         //# END TAG
1741         if (ch=='<' && !cdata && peek(p+1)=='/')
1742             {
1743             break;
1744             }
1745         //# CDATA
1746         p2 = match(p, "<![CDATA[");
1747         if (p2 > p)
1748             {
1749             cdata = true;
1750             p = p2;
1751             continue;
1752             }
1754         //# CHILD ELEMENT
1755         if (ch == '<')
1756             {
1757             p2 = parseElement(p, n, lineNr + countLines(p0, p));
1758             if (p2 == p)
1759                 {
1760                 /*
1761                 printf("problem on element:%s.  p2:%d p:%d\n",
1762                       openTagName.c_str(), p2, p);
1763                 */
1764                 return p0;
1765                 }
1766             p = p2;
1767             continue;
1768             }
1769         //# ENTITY
1770         if (ch=='&' && !cdata)
1771             {
1772             bool found = false;
1773             for (EntityEntry *ee = entities ; ee->value ; ee++)
1774                 {
1775                 int p2 = match(p, ee->escaped);
1776                 if (p2>p)
1777                     {
1778                     data.push_back(ee->value);
1779                     p = p2;
1780                     found = true;
1781                     break;
1782                     }
1783                 }
1784             if (!found)
1785                 {
1786                 error("unterminated entity");
1787                 return -1;
1788                 }
1789             continue;
1790             }
1792         //# NONE OF THE ABOVE
1793         data.push_back(ch);
1794         p++;
1795         }/*while*/
1798     n->value = data;
1799     //printf("%d : data:%s\n",p,data.c_str());
1801     //## Get close tag
1802     p = skipwhite(p);
1803     ch = peek(p);
1804     if (ch != '<')
1805         {
1806         error("no < for end tag\n");
1807         return p0;
1808         }
1809     p++;
1810     ch = peek(p);
1811     if (ch != '/')
1812         {
1813         error("no / on end tag");
1814         return p0;
1815         }
1816     p++;
1817     ch = peek(p);
1818     p = skipwhite(p);
1819     String closeTagName;
1820     p = getWord(p, closeTagName);
1821     if (openTagName != closeTagName)
1822         {
1823         error("Mismatched closing tag.  Expected </%S>. Got '%S'.",
1824                 openTagName.c_str(), closeTagName.c_str());
1825         return p0;
1826         }
1827     p = skipwhite(p);
1828     if (peek(p) != '>')
1829         {
1830         error("no > on end tag for '%s'", closeTagName.c_str());
1831         return p0;
1832         }
1833     p++;
1834     // printf("close element:%s\n",closeTagName.c_str());
1835     p = skipwhite(p);
1836     return p;
1842 Element *Parser::parse(XMLCh *buf,int pos,int len)
1844     parselen = len;
1845     parsebuf = buf;
1846     Element *rootNode = new Element("root");
1847     pos = parseVersion(pos);
1848     pos = parseDoctype(pos);
1849     pos = parseElement(pos, rootNode, 1);
1850     return rootNode;
1854 Element *Parser::parse(const char *buf, int pos, int len)
1856     XMLCh *charbuf = new XMLCh[len + 1];
1857     long i = 0;
1858     for ( ; i < len ; i++)
1859         charbuf[i] = (XMLCh)buf[i];
1860     charbuf[i] = '\0';
1862     Element *n = parse(charbuf, pos, len);
1863     delete[] charbuf;
1864     return n;
1867 Element *Parser::parse(const String &buf)
1869     long len = (long)buf.size();
1870     XMLCh *charbuf = new XMLCh[len + 1];
1871     long i = 0;
1872     for ( ; i < len ; i++)
1873         charbuf[i] = (XMLCh)buf[i];
1874     charbuf[i] = '\0';
1876     Element *n = parse(charbuf, 0, len);
1877     delete[] charbuf;
1878     return n;
1881 Element *Parser::parseFile(const String &fileName)
1884     //##### LOAD INTO A CHAR BUF, THEN CONVERT TO XMLCh
1885     FILE *f = fopen(fileName.c_str(), "rb");
1886     if (!f)
1887         return NULL;
1889     struct stat  statBuf;
1890     if (fstat(fileno(f),&statBuf)<0)
1891         {
1892         fclose(f);
1893         return NULL;
1894         }
1895     long filelen = statBuf.st_size;
1897     //printf("length:%d\n",filelen);
1898     XMLCh *charbuf = new XMLCh[filelen + 1];
1899     for (XMLCh *p=charbuf ; !feof(f) ; p++)
1900         {
1901         *p = (XMLCh)fgetc(f);
1902         }
1903     fclose(f);
1904     charbuf[filelen] = '\0';
1907     /*
1908     printf("nrbytes:%d\n",wc_count);
1909     printf("buf:%ls\n======\n",charbuf);
1910     */
1911     Element *n = parse(charbuf, 0, filelen);
1912     delete[] charbuf;
1913     return n;
1916 //########################################################################
1917 //########################################################################
1918 //##  E N D    X M L
1919 //########################################################################
1920 //########################################################################
1927 //########################################################################
1928 //########################################################################
1929 //##  U R I
1930 //########################################################################
1931 //########################################################################
1933 //This would normally be a call to a UNICODE function
1934 #define isLetter(x) isalpha(x)
1936 /**
1937  *  A class that implements the W3C URI resource reference.
1938  */
1939 class URI
1941 public:
1943     typedef enum
1944         {
1945         SCHEME_NONE =0,
1946         SCHEME_DATA,
1947         SCHEME_HTTP,
1948         SCHEME_HTTPS,
1949         SCHEME_FTP,
1950         SCHEME_FILE,
1951         SCHEME_LDAP,
1952         SCHEME_MAILTO,
1953         SCHEME_NEWS,
1954         SCHEME_TELNET
1955         } SchemeTypes;
1957     /**
1958      *
1959      */
1960     URI()
1961         {
1962         init();
1963         }
1965     /**
1966      *
1967      */
1968     URI(const String &str)
1969         {
1970         init();
1971         parse(str);
1972         }
1975     /**
1976      *
1977      */
1978     URI(const char *str)
1979         {
1980         init();
1981         String domStr = str;
1982         parse(domStr);
1983         }
1986     /**
1987      *
1988      */
1989     URI(const URI &other)
1990         {
1991         init();
1992         assign(other);
1993         }
1996     /**
1997      *
1998      */
1999     URI &operator=(const URI &other)
2000         {
2001         init();
2002         assign(other);
2003         return *this;
2004         }
2007     /**
2008      *
2009      */
2010     virtual ~URI()
2011         {}
2015     /**
2016      *
2017      */
2018     virtual bool parse(const String &str);
2020     /**
2021      *
2022      */
2023     virtual String toString() const;
2025     /**
2026      *
2027      */
2028     virtual int getScheme() const;
2030     /**
2031      *
2032      */
2033     virtual String getSchemeStr() const;
2035     /**
2036      *
2037      */
2038     virtual String getAuthority() const;
2040     /**
2041      *  Same as getAuthority, but if the port has been specified
2042      *  as host:port , the port will not be included
2043      */
2044     virtual String getHost() const;
2046     /**
2047      *
2048      */
2049     virtual int getPort() const;
2051     /**
2052      *
2053      */
2054     virtual String getPath() const;
2056     /**
2057      *
2058      */
2059     virtual String getNativePath() const;
2061     /**
2062      *
2063      */
2064     virtual bool isAbsolute() const;
2066     /**
2067      *
2068      */
2069     virtual bool isOpaque() const;
2071     /**
2072      *
2073      */
2074     virtual String getQuery() const;
2076     /**
2077      *
2078      */
2079     virtual String getFragment() const;
2081     /**
2082      *
2083      */
2084     virtual URI resolve(const URI &other) const;
2086     /**
2087      *
2088      */
2089     virtual void normalize();
2091 private:
2093     /**
2094      *
2095      */
2096     void init()
2097         {
2098         parsebuf  = NULL;
2099         parselen  = 0;
2100         scheme    = SCHEME_NONE;
2101         schemeStr = "";
2102         port      = 0;
2103         authority = "";
2104         path      = "";
2105         absolute  = false;
2106         opaque    = false;
2107         query     = "";
2108         fragment  = "";
2109         }
2112     /**
2113      *
2114      */
2115     void assign(const URI &other)
2116         {
2117         scheme    = other.scheme;
2118         schemeStr = other.schemeStr;
2119         authority = other.authority;
2120         port      = other.port;
2121         path      = other.path;
2122         absolute  = other.absolute;
2123         opaque    = other.opaque;
2124         query     = other.query;
2125         fragment  = other.fragment;
2126         }
2128     int scheme;
2130     String schemeStr;
2132     String authority;
2134     bool portSpecified;
2136     int port;
2138     String path;
2140     bool absolute;
2142     bool opaque;
2144     String query;
2146     String fragment;
2148     void error(const char *fmt, ...);
2150     void trace(const char *fmt, ...);
2153     int peek(int p);
2155     int match(int p, const char *key);
2157     int parseScheme(int p);
2159     int parseHierarchicalPart(int p0);
2161     int parseQuery(int p0);
2163     int parseFragment(int p0);
2165     int parse(int p);
2167     char *parsebuf;
2169     int parselen;
2171 };
2175 typedef struct
2177     int         ival;
2178     const char *sval;
2179     int         port;
2180 } LookupEntry;
2182 LookupEntry schemes[] =
2184     { URI::SCHEME_DATA,   "data:",    0 },
2185     { URI::SCHEME_HTTP,   "http:",   80 },
2186     { URI::SCHEME_HTTPS,  "https:", 443 },
2187     { URI::SCHEME_FTP,    "ftp",     12 },
2188     { URI::SCHEME_FILE,   "file:",    0 },
2189     { URI::SCHEME_LDAP,   "ldap:",  123 },
2190     { URI::SCHEME_MAILTO, "mailto:", 25 },
2191     { URI::SCHEME_NEWS,   "news:",  117 },
2192     { URI::SCHEME_TELNET, "telnet:", 23 },
2193     { 0,                  NULL,       0 }
2194 };
2197 String URI::toString() const
2199     String str = schemeStr;
2200     if (authority.size() > 0)
2201         {
2202         str.append("//");
2203         str.append(authority);
2204         }
2205     str.append(path);
2206     if (query.size() > 0)
2207         {
2208         str.append("?");
2209         str.append(query);
2210         }
2211     if (fragment.size() > 0)
2212         {
2213         str.append("#");
2214         str.append(fragment);
2215         }
2216     return str;
2220 int URI::getScheme() const
2222     return scheme;
2225 String URI::getSchemeStr() const
2227     return schemeStr;
2231 String URI::getAuthority() const
2233     String ret = authority;
2234     if (portSpecified && port>=0)
2235         {
2236         char buf[7];
2237         snprintf(buf, 6, ":%6d", port);
2238         ret.append(buf);
2239         }
2240     return ret;
2243 String URI::getHost() const
2245     return authority;
2248 int URI::getPort() const
2250     return port;
2254 String URI::getPath() const
2256     return path;
2259 String URI::getNativePath() const
2261     String npath;
2262 #ifdef __WIN32__
2263     unsigned int firstChar = 0;
2264     if (path.size() >= 3)
2265         {
2266         if (path[0] == '/' &&
2267             isLetter(path[1]) &&
2268             path[2] == ':')
2269             firstChar++;
2270          }
2271     for (unsigned int i=firstChar ; i<path.size() ; i++)
2272         {
2273         XMLCh ch = (XMLCh) path[i];
2274         if (ch == '/')
2275             npath.push_back((XMLCh)'\\');
2276         else
2277             npath.push_back(ch);
2278         }
2279 #else
2280     npath = path;
2281 #endif
2282     return npath;
2286 bool URI::isAbsolute() const
2288     return absolute;
2291 bool URI::isOpaque() const
2293     return opaque;
2297 String URI::getQuery() const
2299     return query;
2303 String URI::getFragment() const
2305     return fragment;
2309 URI URI::resolve(const URI &other) const
2311     //### According to w3c, this is handled in 3 cases
2313     //## 1
2314     if (opaque || other.isAbsolute())
2315         return other;
2317     //## 2
2318     if (other.fragment.size()  >  0 &&
2319         other.path.size()      == 0 &&
2320         other.scheme           == SCHEME_NONE &&
2321         other.authority.size() == 0 &&
2322         other.query.size()     == 0 )
2323         {
2324         URI fragUri = *this;
2325         fragUri.fragment = other.fragment;
2326         return fragUri;
2327         }
2329     //## 3 http://www.ietf.org/rfc/rfc2396.txt, section 5.2
2330     URI newUri;
2331     //# 3.1
2332     newUri.scheme    = scheme;
2333     newUri.schemeStr = schemeStr;
2334     newUri.query     = other.query;
2335     newUri.fragment  = other.fragment;
2336     if (other.authority.size() > 0)
2337         {
2338         //# 3.2
2339         if (absolute || other.absolute)
2340             newUri.absolute = true;
2341         newUri.authority = other.authority;
2342         newUri.port      = other.port;//part of authority
2343         newUri.path      = other.path;
2344         }
2345     else
2346         {
2347         //# 3.3
2348         if (other.absolute)
2349             {
2350             newUri.absolute = true;
2351             newUri.path     = other.path;
2352             }
2353         else
2354             {
2355             unsigned int pos = path.find_last_of('/');
2356             if (pos != path.npos)
2357                 {
2358                 String tpath = path.substr(0, pos+1);
2359                 tpath.append(other.path);
2360                 newUri.path = tpath;
2361                 }
2362             else
2363                 newUri.path = other.path;
2364             }
2365         }
2367     newUri.normalize();
2368     return newUri;
2373 /**
2374  *  This follows the Java URI algorithm:
2375  *   1. All "." segments are removed.
2376  *   2. If a ".." segment is preceded by a non-".." segment
2377  *          then both of these segments are removed. This step
2378  *          is repeated until it is no longer applicable.
2379  *   3. If the path is relative, and if its first segment
2380  *          contains a colon character (':'), then a "." segment
2381  *          is prepended. This prevents a relative URI with a path
2382  *          such as "a:b/c/d" from later being re-parsed as an
2383  *          opaque URI with a scheme of "a" and a scheme-specific
2384  *          part of "b/c/d". (Deviation from RFC 2396)
2385  */
2386 void URI::normalize()
2388     std::vector<String> segments;
2390     //## Collect segments
2391     if (path.size()<2)
2392         return;
2393     bool abs = false;
2394     unsigned int pos=0;
2395     if (path[0]=='/')
2396         {
2397         abs = true;
2398         pos++;
2399         }
2400     while (pos < path.size())
2401         {
2402         unsigned int pos2 = path.find('/', pos);
2403         if (pos2==path.npos)
2404             {
2405             String seg = path.substr(pos);
2406             //printf("last segment:%s\n", seg.c_str());
2407             segments.push_back(seg);
2408             break;
2409             }
2410         if (pos2>pos)
2411             {
2412             String seg = path.substr(pos, pos2-pos);
2413             //printf("segment:%s\n", seg.c_str());
2414             segments.push_back(seg);
2415             }
2416         pos = pos2;
2417         pos++;
2418         }
2420     //## Clean up (normalize) segments
2421     bool edited = false;
2422     std::vector<String>::iterator iter;
2423     for (iter=segments.begin() ; iter!=segments.end() ; )
2424         {
2425         String s = *iter;
2426         if (s == ".")
2427             {
2428             iter = segments.erase(iter);
2429             edited = true;
2430             }
2431         else if (s == ".." &&
2432                  iter != segments.begin() &&
2433                  *(iter-1) != "..")
2434             {
2435             iter--; //back up, then erase two entries
2436             iter = segments.erase(iter);
2437             iter = segments.erase(iter);
2438             edited = true;
2439             }
2440         else
2441             iter++;
2442         }
2444     //## Rebuild path, if necessary
2445     if (edited)
2446         {
2447         path.clear();
2448         if (abs)
2449             {
2450             path.append("/");
2451             }
2452         std::vector<String>::iterator iter;
2453         for (iter=segments.begin() ; iter!=segments.end() ; iter++)
2454             {
2455             if (iter != segments.begin())
2456                 path.append("/");
2457             path.append(*iter);
2458             }
2459         }
2465 //#########################################################################
2466 //# M E S S A G E S
2467 //#########################################################################
2469 void URI::error(const char *fmt, ...)
2471     va_list args;
2472     fprintf(stderr, "URI error: ");
2473     va_start(args, fmt);
2474     vfprintf(stderr, fmt, args);
2475     va_end(args);
2476     fprintf(stderr, "\n");
2479 void URI::trace(const char *fmt, ...)
2481     va_list args;
2482     fprintf(stdout, "URI: ");
2483     va_start(args, fmt);
2484     vfprintf(stdout, fmt, args);
2485     va_end(args);
2486     fprintf(stdout, "\n");
2492 //#########################################################################
2493 //# P A R S I N G
2494 //#########################################################################
2498 int URI::peek(int p)
2500     if (p<0 || p>=parselen)
2501         return -1;
2502     return parsebuf[p];
2507 int URI::match(int p0, const char *key)
2509     int p = p0;
2510     while (p < parselen)
2511         {
2512         if (*key == '\0')
2513             return p;
2514         else if (*key != parsebuf[p])
2515             break;
2516         p++; key++;
2517         }
2518     return p0;
2521 //#########################################################################
2522 //#  Parsing is performed according to:
2523 //#  http://www.gbiv.com/protocols/uri/rfc/rfc3986.html#components
2524 //#########################################################################
2526 int URI::parseScheme(int p0)
2528     int p = p0;
2529     for (LookupEntry *entry = schemes; entry->sval ; entry++)
2530         {
2531         int p2 = match(p, entry->sval);
2532         if (p2 > p)
2533             {
2534             schemeStr = entry->sval;
2535             scheme    = entry->ival;
2536             port      = entry->port;
2537             p = p2;
2538             return p;
2539             }
2540         }
2542     return p;
2546 int URI::parseHierarchicalPart(int p0)
2548     int p = p0;
2549     int ch;
2551     //# Authority field (host and port, for example)
2552     int p2 = match(p, "//");
2553     if (p2 > p)
2554         {
2555         p = p2;
2556         portSpecified = false;
2557         String portStr;
2558         while (p < parselen)
2559             {
2560             ch = peek(p);
2561             if (ch == '/')
2562                 break;
2563             else if (ch == ':')
2564                 portSpecified = true;
2565             else if (portSpecified)
2566                 portStr.push_back((XMLCh)ch);
2567             else
2568                 authority.push_back((XMLCh)ch);
2569             p++;
2570             }
2571         if (portStr.size() > 0)
2572             {
2573             char *pstr = (char *)portStr.c_str();
2574             char *endStr;
2575             long val = strtol(pstr, &endStr, 10);
2576             if (endStr > pstr) //successful parse?
2577                 port = val;
2578             }
2579         }
2581     //# Are we absolute?
2582     ch = peek(p);
2583     if (isLetter(ch) && peek(p+1)==':')
2584         {
2585         absolute = true;
2586         path.push_back((XMLCh)'/');
2587         }
2588     else if (ch == '/')
2589         {
2590         absolute = true;
2591         if (p>p0) //in other words, if '/' is not the first char
2592             opaque = true;
2593         path.push_back((XMLCh)ch);
2594         p++;
2595         }
2597     while (p < parselen)
2598         {
2599         ch = peek(p);
2600         if (ch == '?' || ch == '#')
2601             break;
2602         path.push_back((XMLCh)ch);
2603         p++;
2604         }
2606     return p;
2609 int URI::parseQuery(int p0)
2611     int p = p0;
2612     int ch = peek(p);
2613     if (ch != '?')
2614         return p0;
2616     p++;
2617     while (p < parselen)
2618         {
2619         ch = peek(p);
2620         if (ch == '#')
2621             break;
2622         query.push_back((XMLCh)ch);
2623         p++;
2624         }
2627     return p;
2630 int URI::parseFragment(int p0)
2633     int p = p0;
2634     int ch = peek(p);
2635     if (ch != '#')
2636         return p0;
2638     p++;
2639     while (p < parselen)
2640         {
2641         ch = peek(p);
2642         if (ch == '?')
2643             break;
2644         fragment.push_back((XMLCh)ch);
2645         p++;
2646         }
2649     return p;
2653 int URI::parse(int p0)
2656     int p = p0;
2658     int p2 = parseScheme(p);
2659     if (p2 < 0)
2660         {
2661         error("Scheme");
2662         return -1;
2663         }
2664     p = p2;
2667     p2 = parseHierarchicalPart(p);
2668     if (p2 < 0)
2669         {
2670         error("Hierarchical part");
2671         return -1;
2672         }
2673     p = p2;
2675     p2 = parseQuery(p);
2676     if (p2 < 0)
2677         {
2678         error("Query");
2679         return -1;
2680         }
2681     p = p2;
2684     p2 = parseFragment(p);
2685     if (p2 < 0)
2686         {
2687         error("Fragment");
2688         return -1;
2689         }
2690     p = p2;
2692     return p;
2698 bool URI::parse(const String &str)
2700     init();
2701     
2702     parselen = str.size();
2704     String tmp;
2705     for (unsigned int i=0 ; i<str.size() ; i++)
2706         {
2707         XMLCh ch = (XMLCh) str[i];
2708         if (ch == '\\')
2709             tmp.push_back((XMLCh)'/');
2710         else
2711             tmp.push_back(ch);
2712         }
2713     parsebuf = (char *) tmp.c_str();
2716     int p = parse(0);
2717     normalize();
2719     if (p < 0)
2720         {
2721         error("Syntax error");
2722         return false;
2723         }
2725     //printf("uri:%s\n", toString().c_str());
2726     //printf("path:%s\n", path.c_str());
2728     return true;
2739 //########################################################################
2740 //########################################################################
2741 //##  M A K E
2742 //########################################################################
2743 //########################################################################
2745 //########################################################################
2746 //# F I L E S E T
2747 //########################################################################
2748 /**
2749  * This is the descriptor for a <fileset> item
2750  */
2751 class FileSet
2753 public:
2755     /**
2756      *
2757      */
2758     FileSet()
2759         {}
2761     /**
2762      *
2763      */
2764     FileSet(const FileSet &other)
2765         { assign(other); }
2767     /**
2768      *
2769      */
2770     FileSet &operator=(const FileSet &other)
2771         { assign(other); return *this; }
2773     /**
2774      *
2775      */
2776     virtual ~FileSet()
2777         {}
2779     /**
2780      *
2781      */
2782     String getDirectory() const
2783         { return directory; }
2784         
2785     /**
2786      *
2787      */
2788     void setDirectory(const String &val)
2789         { directory = val; }
2791     /**
2792      *
2793      */
2794     void setFiles(const std::vector<String> &val)
2795         { files = val; }
2797     /**
2798      *
2799      */
2800     std::vector<String> getFiles() const
2801         { return files; }
2802         
2803     /**
2804      *
2805      */
2806     void setIncludes(const std::vector<String> &val)
2807         { includes = val; }
2809     /**
2810      *
2811      */
2812     std::vector<String> getIncludes() const
2813         { return includes; }
2814         
2815     /**
2816      *
2817      */
2818     void setExcludes(const std::vector<String> &val)
2819         { excludes = val; }
2821     /**
2822      *
2823      */
2824     std::vector<String> getExcludes() const
2825         { return excludes; }
2826         
2827     /**
2828      *
2829      */
2830     unsigned int size() const
2831         { return files.size(); }
2832         
2833     /**
2834      *
2835      */
2836     String operator[](int index) const
2837         { return files[index]; }
2838         
2839     /**
2840      *
2841      */
2842     void clear()
2843         {
2844         directory = "";
2845         files.clear();
2846         includes.clear();
2847         excludes.clear();
2848         }
2849         
2851 private:
2853     void assign(const FileSet &other)
2854         {
2855         directory = other.directory;
2856         files     = other.files;
2857         includes  = other.includes;
2858         excludes  = other.excludes;
2859         }
2861     String directory;
2862     std::vector<String> files;
2863     std::vector<String> includes;
2864     std::vector<String> excludes;
2865 };
2868 //########################################################################
2869 //# F I L E L I S T
2870 //########################################################################
2871 /**
2872  * This is a simpler, explicitly-named list of files
2873  */
2874 class FileList
2876 public:
2878     /**
2879      *
2880      */
2881     FileList()
2882         {}
2884     /**
2885      *
2886      */
2887     FileList(const FileList &other)
2888         { assign(other); }
2890     /**
2891      *
2892      */
2893     FileList &operator=(const FileList &other)
2894         { assign(other); return *this; }
2896     /**
2897      *
2898      */
2899     virtual ~FileList()
2900         {}
2902     /**
2903      *
2904      */
2905     String getDirectory()
2906         { return directory; }
2907         
2908     /**
2909      *
2910      */
2911     void setDirectory(const String &val)
2912         { directory = val; }
2914     /**
2915      *
2916      */
2917     void setFiles(const std::vector<String> &val)
2918         { files = val; }
2920     /**
2921      *
2922      */
2923     std::vector<String> getFiles()
2924         { return files; }
2925         
2926     /**
2927      *
2928      */
2929     unsigned int size()
2930         { return files.size(); }
2931         
2932     /**
2933      *
2934      */
2935     String operator[](int index)
2936         { return files[index]; }
2937         
2938     /**
2939      *
2940      */
2941     void clear()
2942         {
2943         directory = "";
2944         files.clear();
2945         }
2946         
2948 private:
2950     void assign(const FileList &other)
2951         {
2952         directory = other.directory;
2953         files     = other.files;
2954         }
2956     String directory;
2957     std::vector<String> files;
2958 };
2963 //########################################################################
2964 //# M A K E    B A S E
2965 //########################################################################
2966 /**
2967  * Base class for all classes in this file
2968  */
2969 class MakeBase
2971 public:
2973     MakeBase()
2974         { line = 0; }
2975     virtual ~MakeBase()
2976         {}
2978     /**
2979      *     Return the URI of the file associated with this object 
2980      */     
2981     URI getURI()
2982         { return uri; }
2984     /**
2985      * Set the uri to the given string
2986      */
2987     void setURI(const String &uristr)
2988         { uri.parse(uristr); }
2990     /**
2991      *  Resolve another path relative to this one
2992      */
2993     String resolve(const String &otherPath);
2995     /**
2996      * replace variable refs like ${a} with their values
2997      * Assume that the string has already been syntax validated
2998      */
2999     String eval(const String &s, const String &defaultVal);
3001     /**
3002      * replace variable refs like ${a} with their values
3003      * return true or false
3004      * Assume that the string has already been syntax validated
3005      */
3006     bool evalBool(const String &s, bool defaultVal);
3008     /**
3009      *  Get an element attribute, performing substitutions if necessary
3010      */
3011     bool getAttribute(Element *elem, const String &name, String &result);
3013     /**
3014      * Get an element value, performing substitutions if necessary
3015      */
3016     bool getValue(Element *elem, String &result);
3017     
3018     /**
3019      * Set the current line number in the file
3020      */         
3021     void setLine(int val)
3022         { line = val; }
3023         
3024     /**
3025      * Get the current line number in the file
3026      */         
3027     int getLine()
3028         { return line; }
3031     /**
3032      * Set a property to a given value
3033      */
3034     virtual void setProperty(const String &name, const String &val)
3035         {
3036         properties[name] = val;
3037         }
3039     /**
3040      * Return a named property is found, else a null string
3041      */
3042     virtual String getProperty(const String &name)
3043         {
3044         String val;
3045         std::map<String, String>::iterator iter = properties.find(name);
3046         if (iter != properties.end())
3047             val = iter->second;
3048         String sval;
3049         if (!getSubstitutions(val, sval))
3050             return false;
3051         return sval;
3052         }
3054     /**
3055      * Return true if a named property is found, else false
3056      */
3057     virtual bool hasProperty(const String &name)
3058         {
3059         std::map<String, String>::iterator iter = properties.find(name);
3060         if (iter == properties.end())
3061             return false;
3062         return true;
3063         }
3066 protected:
3068     /**
3069      *    The path to the file associated with this object
3070      */     
3071     URI uri;
3072     
3073     /**
3074      *    If this prefix is seen in a substitution, use an environment
3075      *    variable.
3076      *             example:  <property environment="env"/>
3077      *             ${env.JAVA_HOME}
3078      */
3079     String envPrefix;
3081     /**
3082      *    If this prefix is seen in a substitution, use as a
3083      *    pkg-config 'all' query
3084      *             example:  <property pkg-config="pc"/>
3085      *             ${pc.gtkmm}
3086      */
3087     String pcPrefix;
3089     /**
3090      *    If this prefix is seen in a substitution, use as a
3091      *    pkg-config 'cflags' query
3092      *             example:  <property pkg-config="pcc"/>
3093      *             ${pcc.gtkmm}
3094      */
3095     String pccPrefix;
3097     /**
3098      *    If this prefix is seen in a substitution, use as a
3099      *    pkg-config 'libs' query
3100      *             example:  <property pkg-config="pcl"/>
3101      *             ${pcl.gtkmm}
3102      */
3103     String pclPrefix;
3109     /**
3110      *  Print a printf()-like formatted error message
3111      */
3112     void error(const char *fmt, ...);
3114     /**
3115      *  Print a printf()-like formatted trace message
3116      */
3117     void status(const char *fmt, ...);
3119     /**
3120      *  Show target status
3121      */
3122     void targetstatus(const char *fmt, ...);
3124     /**
3125      *  Print a printf()-like formatted trace message
3126      */
3127     void trace(const char *fmt, ...);
3129     /**
3130      *  Check if a given string matches a given regex pattern
3131      */
3132     bool regexMatch(const String &str, const String &pattern);
3134     /**
3135      *
3136      */
3137     String getSuffix(const String &fname);
3139     /**
3140      * Break up a string into substrings delimited the characters
3141      * in delimiters.  Null-length substrings are ignored
3142      */  
3143     std::vector<String> tokenize(const String &val,
3144                           const String &delimiters);
3146     /**
3147      *  replace runs of whitespace with a space
3148      */
3149     String strip(const String &s);
3151     /**
3152      *  remove leading whitespace from each line
3153      */
3154     String leftJustify(const String &s);
3156     /**
3157      *  remove leading and trailing whitespace from string
3158      */
3159     String trim(const String &s);
3161     /**
3162      *  Return a lower case version of the given string
3163      */
3164     String toLower(const String &s);
3166     /**
3167      * Return the native format of the canonical
3168      * path which we store
3169      */
3170     String getNativePath(const String &path);
3172     /**
3173      * Execute a shell command.  Outbuf is a ref to a string
3174      * to catch the result.     
3175      */         
3176     bool executeCommand(const String &call,
3177                         const String &inbuf,
3178                         String &outbuf,
3179                         String &errbuf);
3180     /**
3181      * List all directories in a given base and starting directory
3182      * It is usually called like:
3183      *        bool ret = listDirectories("src", "", result);    
3184      */         
3185     bool listDirectories(const String &baseName,
3186                          const String &dirname,
3187                          std::vector<String> &res);
3189     /**
3190      * Find all files in the named directory 
3191      */         
3192     bool listFiles(const String &baseName,
3193                    const String &dirname,
3194                    std::vector<String> &result);
3196     /**
3197      * Perform a listing for a fileset 
3198      */         
3199     bool listFiles(MakeBase &propRef, FileSet &fileSet);
3201     /**
3202      * Parse a <patternset>
3203      */  
3204     bool parsePatternSet(Element *elem,
3205                        MakeBase &propRef,
3206                        std::vector<String> &includes,
3207                        std::vector<String> &excludes);
3209     /**
3210      * Parse a <fileset> entry, and determine which files
3211      * should be included
3212      */  
3213     bool parseFileSet(Element *elem,
3214                     MakeBase &propRef,
3215                     FileSet &fileSet);
3216     /**
3217      * Parse a <filelist> entry
3218      */  
3219     bool parseFileList(Element *elem,
3220                     MakeBase &propRef,
3221                     FileList &fileList);
3223     /**
3224      * Return this object's property list
3225      */
3226     virtual std::map<String, String> &getProperties()
3227         { return properties; }
3230     std::map<String, String> properties;
3232     /**
3233      * Create a directory, making intermediate dirs
3234      * if necessary
3235      */                  
3236     bool createDirectory(const String &dirname);
3238     /**
3239      * Delete a directory and its children if desired
3240      */
3241     bool removeDirectory(const String &dirName);
3243     /**
3244      * Copy a file from one name to another. Perform only if needed
3245      */ 
3246     bool copyFile(const String &srcFile, const String &destFile);
3248     /**
3249      * Tests if the file exists and is a regular file
3250      */ 
3251     bool isRegularFile(const String &fileName);
3253     /**
3254      * Tests if the file exists and is a directory
3255      */ 
3256     bool isDirectory(const String &fileName);
3258     /**
3259      * Tests is the modification date of fileA is newer than fileB
3260      */ 
3261     bool isNewerThan(const String &fileA, const String &fileB);
3263 private:
3265     bool pkgConfigRecursive(const String packageName,
3266                             const String &path, 
3267                             const String &prefix, 
3268                             int query,
3269                             String &result,
3270                             std::set<String> &deplist);
3272     /**
3273      * utility method to query for "all", "cflags", or "libs" for this package and its
3274      * dependencies.  0, 1, 2
3275      */          
3276     bool pkgConfigQuery(const String &packageName, int query, String &result);
3278     /**
3279      * replace a variable ref like ${a} with a value
3280      */
3281     bool lookupProperty(const String &s, String &result);
3282     
3283     /**
3284      * called by getSubstitutions().  This is in case a looked-up string
3285      * has substitutions also.     
3286      */
3287     bool getSubstitutionsRecursive(const String &s, String &result, int depth);
3289     /**
3290      * replace variable refs in a string like ${a} with their values
3291      */
3292     bool getSubstitutions(const String &s, String &result);
3294     int line;
3297 };
3301 /**
3302  * Define the pkg-config class here, since it will be used in MakeBase method
3303  * implementations. 
3304  */
3305 class PkgConfig : public MakeBase
3308 public:
3310     /**
3311      *
3312      */
3313     PkgConfig()
3314         {
3315          path   = ".";
3316          prefix = "/target";
3317          init();
3318          }
3320     /**
3321      *
3322      */
3323     PkgConfig(const PkgConfig &other)
3324         { assign(other); }
3326     /**
3327      *
3328      */
3329     PkgConfig &operator=(const PkgConfig &other)
3330         { assign(other); return *this; }
3332     /**
3333      *
3334      */
3335     virtual ~PkgConfig()
3336         { }
3338     /**
3339      *
3340      */
3341     virtual String getName()
3342         { return name; }
3344     /**
3345      *
3346      */
3347     virtual String getPath()
3348         { return path; }
3350     /**
3351      *
3352      */
3353     virtual void setPath(const String &val)
3354         { path = val; }
3356     /**
3357      *
3358      */
3359     virtual String getPrefix()
3360         { return prefix; }
3362     /**
3363      *  Allow the user to override the prefix in the file
3364      */
3365     virtual void setPrefix(const String &val)
3366         { prefix = val; }
3368     /**
3369      *
3370      */
3371     virtual String getDescription()
3372         { return description; }
3374     /**
3375      *
3376      */
3377     virtual String getCflags()
3378         { return cflags; }
3380     /**
3381      *
3382      */
3383     virtual String getLibs()
3384         { return libs; }
3386     /**
3387      *
3388      */
3389     virtual String getAll()
3390         {
3391          String ret = cflags;
3392          ret.append(" ");
3393          ret.append(libs);
3394          return ret;
3395         }
3397     /**
3398      *
3399      */
3400     virtual String getVersion()
3401         { return version; }
3403     /**
3404      *
3405      */
3406     virtual int getMajorVersion()
3407         { return majorVersion; }
3409     /**
3410      *
3411      */
3412     virtual int getMinorVersion()
3413         { return minorVersion; }
3415     /**
3416      *
3417      */
3418     virtual int getMicroVersion()
3419         { return microVersion; }
3421     /**
3422      *
3423      */
3424     virtual std::map<String, String> &getAttributes()
3425         { return attrs; }
3427     /**
3428      *
3429      */
3430     virtual std::vector<String> &getRequireList()
3431         { return requireList; }
3433     /**
3434      *  Read a file for its details
3435      */         
3436     virtual bool readFile(const String &fileName);
3438     /**
3439      *  Read a file for its details
3440      */         
3441     virtual bool query(const String &name);
3443 private:
3445     void init()
3446         {
3447         //do not set path and prefix here
3448         name         = "";
3449         description  = "";
3450         cflags       = "";
3451         libs         = "";
3452         requires     = "";
3453         version      = "";
3454         majorVersion = 0;
3455         minorVersion = 0;
3456         microVersion = 0;
3457         fileName     = "";
3458         attrs.clear();
3459         requireList.clear();
3460         }
3462     void assign(const PkgConfig &other)
3463         {
3464         name         = other.name;
3465         path         = other.path;
3466         prefix       = other.prefix;
3467         description  = other.description;
3468         cflags       = other.cflags;
3469         libs         = other.libs;
3470         requires     = other.requires;
3471         version      = other.version;
3472         majorVersion = other.majorVersion;
3473         minorVersion = other.minorVersion;
3474         microVersion = other.microVersion;
3475         fileName     = other.fileName;
3476         attrs        = other.attrs;
3477         requireList  = other.requireList;
3478         }
3482     int get(int pos);
3484     int skipwhite(int pos);
3486     int getword(int pos, String &ret);
3488     /**
3489      * Very important
3490      */         
3491     bool parseRequires();
3493     void parseVersion();
3495     bool parseLine(const String &lineBuf);
3497     bool parse(const String &buf);
3499     void dumpAttrs();
3501     String name;
3503     String path;
3505     String prefix;
3507     String description;
3509     String cflags;
3511     String libs;
3513     String requires;
3515     String version;
3517     int majorVersion;
3519     int minorVersion;
3521     int microVersion;
3523     String fileName;
3525     std::map<String, String> attrs;
3527     std::vector<String> requireList;
3529     char *parsebuf;
3530     int parselen;
3531 };
3536 /**
3537  *  Print a printf()-like formatted error message
3538  */
3539 void MakeBase::error(const char *fmt, ...)
3541     va_list args;
3542     va_start(args,fmt);
3543     fprintf(stderr, "Make error line %d: ", line);
3544     vfprintf(stderr, fmt, args);
3545     fprintf(stderr, "\n");
3546     va_end(args) ;
3551 /**
3552  *  Print a printf()-like formatted trace message
3553  */
3554 void MakeBase::status(const char *fmt, ...)
3556     va_list args;
3557     //fprintf(stdout, " ");
3558     va_start(args,fmt);
3559     vfprintf(stdout, fmt, args);
3560     va_end(args);
3561     fprintf(stdout, "\n");
3562     fflush(stdout);
3566 /**
3567  *  Print a printf()-like formatted trace message
3568  */
3569 void MakeBase::trace(const char *fmt, ...)
3571     va_list args;
3572     fprintf(stdout, "Make: ");
3573     va_start(args,fmt);
3574     vfprintf(stdout, fmt, args);
3575     va_end(args) ;
3576     fprintf(stdout, "\n");
3577     fflush(stdout);
3582 /**
3583  *  Resolve another path relative to this one
3584  */
3585 String MakeBase::resolve(const String &otherPath)
3587     URI otherURI(otherPath);
3588     URI fullURI = uri.resolve(otherURI);
3589     String ret = fullURI.toString();
3590     return ret;
3595 /**
3596  *  Check if a given string matches a given regex pattern
3597  */
3598 bool MakeBase::regexMatch(const String &str, const String &pattern)
3600     const TRexChar *terror = NULL;
3601     const TRexChar *cpat = pattern.c_str();
3602     TRex *expr = trex_compile(cpat, &terror);
3603     if (!expr)
3604         {
3605         if (!terror)
3606             terror = "undefined";
3607         error("compilation error [%s]!\n", terror);
3608         return false;
3609         } 
3611     bool ret = true;
3613     const TRexChar *cstr = str.c_str();
3614     if (trex_match(expr, cstr))
3615         {
3616         ret = true;
3617         }
3618     else
3619         {
3620         ret = false;
3621         }
3623     trex_free(expr);
3625     return ret;
3628 /**
3629  *  Return the suffix, if any, of a file name
3630  */
3631 String MakeBase::getSuffix(const String &fname)
3633     if (fname.size() < 2)
3634         return "";
3635     unsigned int pos = fname.find_last_of('.');
3636     if (pos == fname.npos)
3637         return "";
3638     pos++;
3639     String res = fname.substr(pos, fname.size()-pos);
3640     //trace("suffix:%s", res.c_str()); 
3641     return res;
3646 /**
3647  * Break up a string into substrings delimited the characters
3648  * in delimiters.  Null-length substrings are ignored
3649  */  
3650 std::vector<String> MakeBase::tokenize(const String &str,
3651                                 const String &delimiters)
3654     std::vector<String> res;
3655     char *del = (char *)delimiters.c_str();
3656     String dmp;
3657     for (unsigned int i=0 ; i<str.size() ; i++)
3658         {
3659         char ch = str[i];
3660         char *p = (char *)0;
3661         for (p=del ; *p ; p++)
3662             if (*p == ch)
3663                 break;
3664         if (*p)
3665             {
3666             if (dmp.size() > 0)
3667                 {
3668                 res.push_back(dmp);
3669                 dmp.clear();
3670                 }
3671             }
3672         else
3673             {
3674             dmp.push_back(ch);
3675             }
3676         }
3677     //Add tail
3678     if (dmp.size() > 0)
3679         {
3680         res.push_back(dmp);
3681         dmp.clear();
3682         }
3684     return res;
3689 /**
3690  *  replace runs of whitespace with a single space
3691  */
3692 String MakeBase::strip(const String &s)
3694     int len = s.size();
3695     String stripped;
3696     for (int i = 0 ; i<len ; i++)
3697         {
3698         char ch = s[i];
3699         if (isspace(ch))
3700             {
3701             stripped.push_back(' ');
3702             for ( ; i<len ; i++)
3703                 {
3704                 ch = s[i];
3705                 if (!isspace(ch))
3706                     {
3707                     stripped.push_back(ch);
3708                     break;
3709                     }
3710                 }
3711             }
3712         else
3713             {
3714             stripped.push_back(ch);
3715             }
3716         }
3717     return stripped;
3720 /**
3721  *  remove leading whitespace from each line
3722  */
3723 String MakeBase::leftJustify(const String &s)
3725     String out;
3726     int len = s.size();
3727     for (int i = 0 ; i<len ; )
3728         {
3729         char ch;
3730         //Skip to first visible character
3731         while (i<len)
3732             {
3733             ch = s[i];
3734             if (ch == '\n' || ch == '\r'
3735               || !isspace(ch))
3736                   break;
3737             i++;
3738             }
3739         //Copy the rest of the line
3740         while (i<len)
3741             {
3742             ch = s[i];
3743             if (ch == '\n' || ch == '\r')
3744                 {
3745                 if (ch != '\r')
3746                     out.push_back('\n');
3747                 i++;
3748                 break;
3749                 }
3750             else
3751                 {
3752                 out.push_back(ch);
3753                 }
3754             i++;
3755             }
3756         }
3757     return out;
3761 /**
3762  *  Removes whitespace from beginning and end of a string
3763  */
3764 String MakeBase::trim(const String &s)
3766     if (s.size() < 1)
3767         return s;
3768     
3769     //Find first non-ws char
3770     unsigned int begin = 0;
3771     for ( ; begin < s.size() ; begin++)
3772         {
3773         if (!isspace(s[begin]))
3774             break;
3775         }
3777     //Find first non-ws char, going in reverse
3778     unsigned int end = s.size() - 1;
3779     for ( ; end > begin ; end--)
3780         {
3781         if (!isspace(s[end]))
3782             break;
3783         }
3784     //trace("begin:%d  end:%d", begin, end);
3786     String res = s.substr(begin, end-begin+1);
3787     return res;
3791 /**
3792  *  Return a lower case version of the given string
3793  */
3794 String MakeBase::toLower(const String &s)
3796     if (s.size()==0)
3797         return s;
3799     String ret;
3800     for(unsigned int i=0; i<s.size() ; i++)
3801         {
3802         ret.push_back(tolower(s[i]));
3803         }
3804     return ret;
3808 /**
3809  * Return the native format of the canonical
3810  * path which we store
3811  */
3812 String MakeBase::getNativePath(const String &path)
3814 #ifdef __WIN32__
3815     String npath;
3816     unsigned int firstChar = 0;
3817     if (path.size() >= 3)
3818         {
3819         if (path[0] == '/' &&
3820             isalpha(path[1]) &&
3821             path[2] == ':')
3822             firstChar++;
3823         }
3824     for (unsigned int i=firstChar ; i<path.size() ; i++)
3825         {
3826         char ch = path[i];
3827         if (ch == '/')
3828             npath.push_back('\\');
3829         else
3830             npath.push_back(ch);
3831         }
3832     return npath;
3833 #else
3834     return path;
3835 #endif
3839 #ifdef __WIN32__
3840 #include <tchar.h>
3842 static String win32LastError()
3845     DWORD dw = GetLastError(); 
3847     LPVOID str;
3848     FormatMessage(
3849         FORMAT_MESSAGE_ALLOCATE_BUFFER | 
3850         FORMAT_MESSAGE_FROM_SYSTEM,
3851         NULL,
3852         dw,
3853         0,
3854         (LPTSTR) &str,
3855         0, NULL );
3856     LPTSTR p = _tcschr((const char *)str, _T('\r'));
3857     if(p != NULL)
3858         { // lose CRLF
3859         *p = _T('\0');
3860         }
3861     String ret = (char *)str;
3862     LocalFree(str);
3864     return ret;
3866 #endif
3871 #ifdef __WIN32__
3873 /**
3874  * Execute a system call, using pipes to send data to the
3875  * program's stdin,  and reading stdout and stderr.
3876  */
3877 bool MakeBase::executeCommand(const String &command,
3878                               const String &inbuf,
3879                               String &outbuf,
3880                               String &errbuf)
3883     status("============ cmd ============\n%s\n=============================",
3884                 command.c_str());
3886     outbuf.clear();
3887     errbuf.clear();
3888     
3890     /*
3891     I really hate having win32 code in this program, but the
3892     read buffer in command.com and cmd.exe are just too small
3893     for the large commands we need for compiling and linking.
3894     */
3896     bool ret = true;
3898     //# Allocate a separate buffer for safety
3899     char *paramBuf = new char[command.size() + 1];
3900     if (!paramBuf)
3901        {
3902        error("executeCommand cannot allocate command buffer");
3903        return false;
3904        }
3905     strcpy(paramBuf, (char *)command.c_str());
3907     //# Go to http://msdn2.microsoft.com/en-us/library/ms682499.aspx
3908     //# to see how Win32 pipes work
3910     //# Create pipes
3911     SECURITY_ATTRIBUTES saAttr; 
3912     saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); 
3913     saAttr.bInheritHandle = TRUE; 
3914     saAttr.lpSecurityDescriptor = NULL; 
3915     HANDLE stdinRead,  stdinWrite;
3916     HANDLE stdoutRead, stdoutWrite;
3917     HANDLE stderrRead, stderrWrite;
3918     if (!CreatePipe(&stdinRead, &stdinWrite, &saAttr, 0))
3919         {
3920         error("executeProgram: could not create pipe");
3921         delete[] paramBuf;
3922         return false;
3923         } 
3924     SetHandleInformation(stdinWrite, HANDLE_FLAG_INHERIT, 0);
3925     if (!CreatePipe(&stdoutRead, &stdoutWrite, &saAttr, 0))
3926         {
3927         error("executeProgram: could not create pipe");
3928         delete[] paramBuf;
3929         return false;
3930         } 
3931     SetHandleInformation(stdoutRead, HANDLE_FLAG_INHERIT, 0);
3932     if (!CreatePipe(&stderrRead, &stderrWrite, &saAttr, 0))
3933         {
3934         error("executeProgram: could not create pipe");
3935         delete[] paramBuf;
3936         return false;
3937         } 
3938     SetHandleInformation(stderrRead, HANDLE_FLAG_INHERIT, 0);
3940     // Create the process
3941     STARTUPINFO siStartupInfo;
3942     PROCESS_INFORMATION piProcessInfo;
3943     memset(&siStartupInfo, 0, sizeof(siStartupInfo));
3944     memset(&piProcessInfo, 0, sizeof(piProcessInfo));
3945     siStartupInfo.cb = sizeof(siStartupInfo);
3946     siStartupInfo.hStdError   =  stderrWrite;
3947     siStartupInfo.hStdOutput  =  stdoutWrite;
3948     siStartupInfo.hStdInput   =  stdinRead;
3949     siStartupInfo.dwFlags    |=  STARTF_USESTDHANDLES;
3950    
3951     if (!CreateProcess(NULL, paramBuf, NULL, NULL, true,
3952                 0, NULL, NULL, &siStartupInfo,
3953                 &piProcessInfo))
3954         {
3955         error("executeCommand : could not create process : %s",
3956                     win32LastError().c_str());
3957         ret = false;
3958         }
3960     delete[] paramBuf;
3962     DWORD bytesWritten;
3963     if (inbuf.size()>0 &&
3964         !WriteFile(stdinWrite, inbuf.c_str(), inbuf.size(), 
3965                &bytesWritten, NULL))
3966         {
3967         error("executeCommand: could not write to pipe");
3968         return false;
3969         }    
3970     if (!CloseHandle(stdinWrite))
3971         {          
3972         error("executeCommand: could not close write pipe");
3973         return false;
3974         }
3975     if (!CloseHandle(stdoutWrite))
3976         {
3977         error("executeCommand: could not close read pipe");
3978         return false;
3979         }
3980     if (!CloseHandle(stderrWrite))
3981         {
3982         error("executeCommand: could not close read pipe");
3983         return false;
3984         }
3986     bool lastLoop = false;
3987     while (true)
3988         {
3989         DWORD avail;
3990         DWORD bytesRead;
3991         char readBuf[4096];
3993         //trace("## stderr");
3994         PeekNamedPipe(stderrRead, NULL, 0, NULL, &avail, NULL);
3995         if (avail > 0)
3996             {
3997             bytesRead = 0;
3998             if (avail>4096) avail = 4096;
3999             ReadFile(stderrRead, readBuf, avail, &bytesRead, NULL);
4000             if (bytesRead > 0)
4001                 {
4002                 for (unsigned int i=0 ; i<bytesRead ; i++)
4003                     errbuf.push_back(readBuf[i]);
4004                 }
4005             }
4007         //trace("## stdout");
4008         PeekNamedPipe(stdoutRead, NULL, 0, NULL, &avail, NULL);
4009         if (avail > 0)
4010             {
4011             bytesRead = 0;
4012             if (avail>4096) avail = 4096;
4013             ReadFile(stdoutRead, readBuf, avail, &bytesRead, NULL);
4014             if (bytesRead > 0)
4015                 {
4016                 for (unsigned int i=0 ; i<bytesRead ; i++)
4017                     outbuf.push_back(readBuf[i]);
4018                 }
4019             }
4020             
4021         //Was this the final check after program done?
4022         if (lastLoop)
4023             break;
4025         DWORD exitCode;
4026         GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
4027         if (exitCode != STILL_ACTIVE)
4028             lastLoop = true;
4030         Sleep(10);
4031         }    
4032     //trace("outbuf:%s", outbuf.c_str());
4033     if (!CloseHandle(stdoutRead))
4034         {
4035         error("executeCommand: could not close read pipe");
4036         return false;
4037         }
4038     if (!CloseHandle(stderrRead))
4039         {
4040         error("executeCommand: could not close read pipe");
4041         return false;
4042         }
4044     DWORD exitCode;
4045     GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
4046     //trace("exit code:%d", exitCode);
4047     if (exitCode != 0)
4048         {
4049         ret = false;
4050         }
4051     
4052     CloseHandle(piProcessInfo.hProcess);
4053     CloseHandle(piProcessInfo.hThread);
4055     return ret;
4057
4059 #else  /*do it unix style*/
4061 #include <sys/wait.h>
4065 /**
4066  * Execute a system call, using pipes to send data to the
4067  * program's stdin,  and reading stdout and stderr.
4068  */
4069 bool MakeBase::executeCommand(const String &command,
4070                               const String &inbuf,
4071                               String &outbuf,
4072                               String &errbuf)
4075     status("============ cmd ============\n%s\n=============================",
4076                 command.c_str());
4078     outbuf.clear();
4079     errbuf.clear();
4080     
4082     int outfds[2];
4083     if (pipe(outfds) < 0)
4084         return false;
4085     int errfds[2];
4086     if (pipe(errfds) < 0)
4087         return false;
4088     int pid = fork();
4089     if (pid < 0)
4090         {
4091         close(outfds[0]);
4092         close(outfds[1]);
4093         close(errfds[0]);
4094         close(errfds[1]);
4095         error("launch of command '%s' failed : %s",
4096              command.c_str(), strerror(errno));
4097         return false;
4098         }
4099     else if (pid > 0) // parent
4100         {
4101         close(outfds[1]);
4102         close(errfds[1]);
4103         }
4104     else // == 0, child
4105         {
4106         close(outfds[0]);
4107         dup2(outfds[1], STDOUT_FILENO);
4108         close(outfds[1]);
4109         close(errfds[0]);
4110         dup2(errfds[1], STDERR_FILENO);
4111         close(errfds[1]);
4113         char *args[4];
4114         args[0] = (char *)"sh";
4115         args[1] = (char *)"-c";
4116         args[2] = (char *)command.c_str();
4117         args[3] = NULL;
4118         execv("/bin/sh", args);
4119         exit(EXIT_FAILURE);
4120         }
4122     String outb;
4123     String errb;
4125     int outRead = outfds[0];
4126     int errRead = errfds[0];
4127     int max = outRead;
4128     if (errRead > max)
4129         max = errRead;
4131     bool outOpen = true;
4132     bool errOpen = true;
4134     while (outOpen || errOpen)
4135         {
4136         char ch;
4137         fd_set fdset;
4138         FD_ZERO(&fdset);
4139         if (outOpen)
4140             FD_SET(outRead, &fdset);
4141         if (errOpen)
4142             FD_SET(errRead, &fdset);
4143         int ret = select(max+1, &fdset, NULL, NULL, NULL);
4144         if (ret < 0)
4145             break;
4146         if (FD_ISSET(outRead, &fdset))
4147             {
4148             if (read(outRead, &ch, 1) <= 0)
4149                 { outOpen = false; }
4150             else if (ch <= 0)
4151                 { /* outOpen = false; */ }
4152             else
4153                 { outb.push_back(ch); }
4154             }
4155         if (FD_ISSET(errRead, &fdset))
4156             {
4157             if (read(errRead, &ch, 1) <= 0)
4158                 { errOpen = false; }
4159             else if (ch <= 0)
4160                 { /* errOpen = false; */ }
4161             else
4162                 { errb.push_back(ch); }
4163             }
4164         }
4166     int childReturnValue;
4167     wait(&childReturnValue);
4169     close(outRead);
4170     close(errRead);
4172     outbuf = outb;
4173     errbuf = errb;
4175     if (childReturnValue != 0)
4176         {
4177         error("exec of command '%s' failed : %s",
4178              command.c_str(), strerror(childReturnValue));
4179         return false;
4180         }
4182     return true;
4183
4185 #endif
4190 bool MakeBase::listDirectories(const String &baseName,
4191                               const String &dirName,
4192                               std::vector<String> &res)
4194     res.push_back(dirName);
4195     String fullPath = baseName;
4196     if (dirName.size()>0)
4197         {
4198         fullPath.append("/");
4199         fullPath.append(dirName);
4200         }
4201     DIR *dir = opendir(fullPath.c_str());
4202     while (true)
4203         {
4204         struct dirent *de = readdir(dir);
4205         if (!de)
4206             break;
4208         //Get the directory member name
4209         String s = de->d_name;
4210         if (s.size() == 0 || s[0] == '.')
4211             continue;
4212         String childName = dirName;
4213         childName.append("/");
4214         childName.append(s);
4216         String fullChildPath = baseName;
4217         fullChildPath.append("/");
4218         fullChildPath.append(childName);
4219         struct stat finfo;
4220         String childNative = getNativePath(fullChildPath);
4221         if (stat(childNative.c_str(), &finfo)<0)
4222             {
4223             error("cannot stat file:%s", childNative.c_str());
4224             }
4225         else if (S_ISDIR(finfo.st_mode))
4226             {
4227             //trace("directory: %s", childName.c_str());
4228             if (!listDirectories(baseName, childName, res))
4229                 return false;
4230             }
4231         }
4232     closedir(dir);
4234     return true;
4238 bool MakeBase::listFiles(const String &baseDir,
4239                          const String &dirName,
4240                          std::vector<String> &res)
4242     String fullDir = baseDir;
4243     if (dirName.size()>0)
4244         {
4245         fullDir.append("/");
4246         fullDir.append(dirName);
4247         }
4248     String dirNative = getNativePath(fullDir);
4250     std::vector<String> subdirs;
4251     DIR *dir = opendir(dirNative.c_str());
4252     if (!dir)
4253         {
4254         error("Could not open directory %s : %s",
4255               dirNative.c_str(), strerror(errno));
4256         return false;
4257         }
4258     while (true)
4259         {
4260         struct dirent *de = readdir(dir);
4261         if (!de)
4262             break;
4264         //Get the directory member name
4265         String s = de->d_name;
4266         if (s.size() == 0 || s[0] == '.')
4267             continue;
4268         String childName;
4269         if (dirName.size()>0)
4270             {
4271             childName.append(dirName);
4272             childName.append("/");
4273             }
4274         childName.append(s);
4275         String fullChild = baseDir;
4276         fullChild.append("/");
4277         fullChild.append(childName);
4278         
4279         if (isDirectory(fullChild))
4280             {
4281             //trace("directory: %s", childName.c_str());
4282             if (!listFiles(baseDir, childName, res))
4283                 return false;
4284             continue;
4285             }
4286         else if (!isRegularFile(fullChild))
4287             {
4288             error("unknown file:%s", childName.c_str());
4289             return false;
4290             }
4292        //all done!
4293         res.push_back(childName);
4295         }
4296     closedir(dir);
4298     return true;
4302 /**
4303  * Several different classes extend MakeBase.  By "propRef", we mean
4304  * the one holding the properties.  Likely "Make" itself
4305  */
4306 bool MakeBase::listFiles(MakeBase &propRef, FileSet &fileSet)
4308     //before doing the list,  resolve any property references
4309     //that might have been specified in the directory name, such as ${src}
4310     String fsDir = fileSet.getDirectory();
4311     String dir;
4312     if (!propRef.getSubstitutions(fsDir, dir))
4313         return false;
4314     String baseDir = propRef.resolve(dir);
4315     std::vector<String> fileList;
4316     if (!listFiles(baseDir, "", fileList))
4317         return false;
4319     std::vector<String> includes = fileSet.getIncludes();
4320     std::vector<String> excludes = fileSet.getExcludes();
4322     std::vector<String> incs;
4323     std::vector<String>::iterator iter;
4325     std::sort(fileList.begin(), fileList.end());
4327     //If there are <includes>, then add files to the output
4328     //in the order of the include list
4329     if (includes.size()==0)
4330         incs = fileList;
4331     else
4332         {
4333         for (iter = includes.begin() ; iter != includes.end() ; iter++)
4334             {
4335             String &pattern = *iter;
4336             std::vector<String>::iterator siter;
4337             for (siter = fileList.begin() ; siter != fileList.end() ; siter++)
4338                 {
4339                 String s = *siter;
4340                 if (regexMatch(s, pattern))
4341                     {
4342                     //trace("INCLUDED:%s", s.c_str());
4343                     incs.push_back(s);
4344                     }
4345                 }
4346             }
4347         }
4349     //Now trim off the <excludes>
4350     std::vector<String> res;
4351     for (iter = incs.begin() ; iter != incs.end() ; iter++)
4352         {
4353         String s = *iter;
4354         bool skipme = false;
4355         std::vector<String>::iterator siter;
4356         for (siter = excludes.begin() ; siter != excludes.end() ; siter++)
4357             {
4358             String &pattern = *siter;
4359             if (regexMatch(s, pattern))
4360                 {
4361                 //trace("EXCLUDED:%s", s.c_str());
4362                 skipme = true;
4363                 break;
4364                 }
4365             }
4366         if (!skipme)
4367             res.push_back(s);
4368         }
4369         
4370     fileSet.setFiles(res);
4372     return true;
4376 /**
4377  * 0 == all, 1 = cflags, 2 = libs
4378  */ 
4379 bool MakeBase::pkgConfigRecursive(const String packageName,
4380                                   const String &path, 
4381                                   const String &prefix, 
4382                                   int query,
4383                                   String &result,
4384                                   std::set<String> &deplist) 
4386     PkgConfig pkgConfig;
4387     if (path.size() > 0)
4388         pkgConfig.setPath(path);
4389     if (prefix.size() > 0)
4390         pkgConfig.setPrefix(prefix);
4391     if (!pkgConfig.query(packageName))
4392         return false;
4393     if (query == 0)
4394         result = pkgConfig.getAll();
4395     else if (query == 1)
4396         result = pkgConfig.getCflags();
4397     else
4398         result = pkgConfig.getLibs();
4399     deplist.insert(packageName);
4400     std::vector<String> list = pkgConfig.getRequireList();
4401     for (unsigned int i = 0 ; i<list.size() ; i++)
4402         {
4403         String depPkgName = list[i];
4404         if (deplist.find(depPkgName) != deplist.end())
4405             continue;
4406         String val;
4407         if (!pkgConfigRecursive(depPkgName, path, prefix, query, val, deplist))
4408             {
4409             error("Based on 'requires' attribute of package '%s'", packageName.c_str());
4410             return false;
4411             }
4412         result.append(" ");
4413         result.append(val);
4414         }
4416     return true;
4419 bool MakeBase::pkgConfigQuery(const String &packageName, int query, String &result)
4421     std::set<String> deplist;
4422     String path = getProperty("pkg-config-path");
4423     if (path.size()>0)
4424         path = resolve(path);
4425     String prefix = getProperty("pkg-config-prefix");
4426     String val;
4427     if (!pkgConfigRecursive(packageName, path, prefix, query, val, deplist))
4428         return false;
4429     result = val;
4430     return true;
4435 /**
4436  * replace a variable ref like ${a} with a value
4437  */
4438 bool MakeBase::lookupProperty(const String &propertyName, String &result)
4440     String varname = propertyName;
4441     if (envPrefix.size() > 0 &&
4442         varname.compare(0, envPrefix.size(), envPrefix) == 0)
4443         {
4444         varname = varname.substr(envPrefix.size());
4445         char *envstr = getenv(varname.c_str());
4446         if (!envstr)
4447             {
4448             error("environment variable '%s' not defined", varname.c_str());
4449             return false;
4450             }
4451         result = envstr;
4452         }
4453     else if (pcPrefix.size() > 0 &&
4454         varname.compare(0, pcPrefix.size(), pcPrefix) == 0)
4455         {
4456         varname = varname.substr(pcPrefix.size());
4457         String val;
4458         if (!pkgConfigQuery(varname, 0, val))
4459             return false;
4460         result = val;
4461         }
4462     else if (pccPrefix.size() > 0 &&
4463         varname.compare(0, pccPrefix.size(), pccPrefix) == 0)
4464         {
4465         varname = varname.substr(pccPrefix.size());
4466         String val;
4467         if (!pkgConfigQuery(varname, 1, val))
4468             return false;
4469         result = val;
4470         }
4471     else if (pclPrefix.size() > 0 &&
4472         varname.compare(0, pclPrefix.size(), pclPrefix) == 0)
4473         {
4474         varname = varname.substr(pclPrefix.size());
4475         String val;
4476         if (!pkgConfigQuery(varname, 2, val))
4477             return false;
4478         result = val;
4479         }
4480     else
4481         {
4482         std::map<String, String>::iterator iter;
4483         iter = properties.find(varname);
4484         if (iter != properties.end())
4485             {
4486             result = iter->second;
4487             }
4488         else
4489             {
4490             error("property '%s' not found", varname.c_str());
4491             return false;
4492             }
4493         }
4494     return true;
4500 /**
4501  * Analyse a string, looking for any substitutions or other
4502  * things that need resolution 
4503  */
4504 bool MakeBase::getSubstitutionsRecursive(const String &str,
4505                                          String &result, int depth)
4507     if (depth > 10)
4508         {
4509         error("nesting of substitutions too deep (>10) for '%s'",
4510                         str.c_str());
4511         return false;
4512         }
4513     String s = trim(str);
4514     int len = (int)s.size();
4515     String val;
4516     for (int i=0 ; i<len ; i++)
4517         {
4518         char ch = s[i];
4519         if (ch == '$' && s[i+1] == '{')
4520             {
4521             String varname;
4522             int j = i+2;
4523             for ( ; j<len ; j++)
4524                 {
4525                 ch = s[j];
4526                 if (ch == '$' && s[j+1] == '{')
4527                     {
4528                     error("attribute %s cannot have nested variable references",
4529                            s.c_str());
4530                     return false;
4531                     }
4532                 else if (ch == '}')
4533                     {
4534                     varname = trim(varname);
4535                     String varval;
4536                     if (!lookupProperty(varname, varval))
4537                         return false;
4538                     String varval2;
4539                     //Now see if the answer has ${} in it, too
4540                     if (!getSubstitutionsRecursive(varval, varval2, depth + 1))
4541                         return false;
4542                     val.append(varval2);
4543                     break;
4544                     }
4545                 else
4546                     {
4547                     varname.push_back(ch);
4548                     }
4549                 }
4550             i = j;
4551             }
4552         else
4553             {
4554             val.push_back(ch);
4555             }
4556         }
4557     result = val;
4558     return true;
4561 /**
4562  * Analyse a string, looking for any substitutions or other
4563  * things that need resilution 
4564  */
4565 bool MakeBase::getSubstitutions(const String &str, String &result)
4567     return getSubstitutionsRecursive(str, result, 0);
4572 /**
4573  * replace variable refs like ${a} with their values
4574  * Assume that the string has already been syntax validated
4575  */
4576 String MakeBase::eval(const String &s, const String &defaultVal)
4578     if (s.size()==0)
4579         return defaultVal;
4580     String ret;
4581     if (getSubstitutions(s, ret))
4582         return ret;
4583     else
4584         return defaultVal;
4588 /**
4589  * replace variable refs like ${a} with their values
4590  * return true or false
4591  * Assume that the string has already been syntax validated
4592  */
4593 bool MakeBase::evalBool(const String &s, bool defaultVal)
4595     if (s.size()==0)
4596         return defaultVal;
4597     String val = eval(s, "false");
4598     if (val.size()==0)
4599         return defaultVal;
4600     if (val == "true" || val == "TRUE")
4601         return true;
4602     else
4603         return false;
4607 /**
4608  * Get a string attribute, testing it for proper syntax and
4609  * property names.
4610  */
4611 bool MakeBase::getAttribute(Element *elem, const String &name,
4612                                     String &result)
4614     String s = elem->getAttribute(name);
4615     String tmp;
4616     bool ret = getSubstitutions(s, tmp);
4617     if (ret)
4618         result = s;  //assign -if- ok
4619     return ret;
4623 /**
4624  * Get a string value, testing it for proper syntax and
4625  * property names.
4626  */
4627 bool MakeBase::getValue(Element *elem, String &result)
4629     String s = elem->getValue();
4630     String tmp;
4631     bool ret = getSubstitutions(s, tmp);
4632     if (ret)
4633         result = s;  //assign -if- ok
4634     return ret;
4640 /**
4641  * Parse a <patternset> entry
4642  */  
4643 bool MakeBase::parsePatternSet(Element *elem,
4644                           MakeBase &propRef,
4645                           std::vector<String> &includes,
4646                           std::vector<String> &excludes
4647                           )
4649     std::vector<Element *> children  = elem->getChildren();
4650     for (unsigned int i=0 ; i<children.size() ; i++)
4651         {
4652         Element *child = children[i];
4653         String tagName = child->getName();
4654         if (tagName == "exclude")
4655             {
4656             String fname;
4657             if (!propRef.getAttribute(child, "name", fname))
4658                 return false;
4659             //trace("EXCLUDE: %s", fname.c_str());
4660             excludes.push_back(fname);
4661             }
4662         else if (tagName == "include")
4663             {
4664             String fname;
4665             if (!propRef.getAttribute(child, "name", fname))
4666                 return false;
4667             //trace("INCLUDE: %s", fname.c_str());
4668             includes.push_back(fname);
4669             }
4670         }
4672     return true;
4678 /**
4679  * Parse a <fileset> entry, and determine which files
4680  * should be included
4681  */  
4682 bool MakeBase::parseFileSet(Element *elem,
4683                           MakeBase &propRef,
4684                           FileSet &fileSet)
4686     String name = elem->getName();
4687     if (name != "fileset")
4688         {
4689         error("expected <fileset>");
4690         return false;
4691         }
4694     std::vector<String> includes;
4695     std::vector<String> excludes;
4697     //A fileset has one implied patternset
4698     if (!parsePatternSet(elem, propRef, includes, excludes))
4699         {
4700         return false;
4701         }
4702     //Look for child tags, including more patternsets
4703     std::vector<Element *> children  = elem->getChildren();
4704     for (unsigned int i=0 ; i<children.size() ; i++)
4705         {
4706         Element *child = children[i];
4707         String tagName = child->getName();
4708         if (tagName == "patternset")
4709             {
4710             if (!parsePatternSet(child, propRef, includes, excludes))
4711                 {
4712                 return false;
4713                 }
4714             }
4715         }
4717     String dir;
4718     //Now do the stuff
4719     //Get the base directory for reading file names
4720     if (!propRef.getAttribute(elem, "dir", dir))
4721         return false;
4723     fileSet.setDirectory(dir);
4724     fileSet.setIncludes(includes);
4725     fileSet.setExcludes(excludes);
4726     
4727     /*
4728     std::vector<String> fileList;
4729     if (dir.size() > 0)
4730         {
4731         String baseDir = propRef.resolve(dir);
4732         if (!listFiles(baseDir, "", includes, excludes, fileList))
4733             return false;
4734         }
4735     std::sort(fileList.begin(), fileList.end());
4736     result = fileList;
4737     */
4739     
4740     /*
4741     for (unsigned int i=0 ; i<result.size() ; i++)
4742         {
4743         trace("RES:%s", result[i].c_str());
4744         }
4745     */
4747     
4748     return true;
4751 /**
4752  * Parse a <filelist> entry.  This is far simpler than FileSet,
4753  * since no directory scanning is needed.  The file names are listed
4754  * explicitly.
4755  */  
4756 bool MakeBase::parseFileList(Element *elem,
4757                           MakeBase &propRef,
4758                           FileList &fileList)
4760     std::vector<String> fnames;
4761     //Look for child tags, namely "file"
4762     std::vector<Element *> children  = elem->getChildren();
4763     for (unsigned int i=0 ; i<children.size() ; i++)
4764         {
4765         Element *child = children[i];
4766         String tagName = child->getName();
4767         if (tagName == "file")
4768             {
4769             String fname = child->getAttribute("name");
4770             if (fname.size()==0)
4771                 {
4772                 error("<file> element requires name="" attribute");
4773                 return false;
4774                 }
4775             fnames.push_back(fname);
4776             }
4777         else
4778             {
4779             error("tag <%s> not allowed in <fileset>", tagName.c_str());
4780             return false;
4781             }
4782         }
4784     String dir;
4785     //Get the base directory for reading file names
4786     if (!propRef.getAttribute(elem, "dir", dir))
4787         return false;
4788     fileList.setDirectory(dir);
4789     fileList.setFiles(fnames);
4791     return true;
4796 /**
4797  * Create a directory, making intermediate dirs
4798  * if necessary
4799  */                  
4800 bool MakeBase::createDirectory(const String &dirname)
4802     //trace("## createDirectory: %s", dirname.c_str());
4803     //## first check if it exists
4804     struct stat finfo;
4805     String nativeDir = getNativePath(dirname);
4806     char *cnative = (char *) nativeDir.c_str();
4807 #ifdef __WIN32__
4808     if (strlen(cnative)==2 && cnative[1]==':')
4809         return true;
4810 #endif
4811     if (stat(cnative, &finfo)==0)
4812         {
4813         if (!S_ISDIR(finfo.st_mode))
4814             {
4815             error("mkdir: file %s exists but is not a directory",
4816                   cnative);
4817             return false;
4818             }
4819         else //exists
4820             {
4821             return true;
4822             }
4823         }
4825     //## 2: pull off the last path segment, if any,
4826     //## to make the dir 'above' this one, if necessary
4827     unsigned int pos = dirname.find_last_of('/');
4828     if (pos>0 && pos != dirname.npos)
4829         {
4830         String subpath = dirname.substr(0, pos);
4831         //A letter root (c:) ?
4832         if (!createDirectory(subpath))
4833             return false;
4834         }
4835         
4836     //## 3: now make
4837 #ifdef __WIN32__
4838     if (mkdir(cnative)<0)
4839 #else
4840     if (mkdir(cnative, S_IRWXU | S_IRWXG | S_IRWXO)<0)
4841 #endif
4842         {
4843         error("cannot make directory '%s' : %s",
4844                  cnative, strerror(errno));
4845         return false;
4846         }
4847         
4848     return true;
4852 /**
4853  * Remove a directory recursively
4854  */ 
4855 bool MakeBase::removeDirectory(const String &dirName)
4857     char *dname = (char *)dirName.c_str();
4859     DIR *dir = opendir(dname);
4860     if (!dir)
4861         {
4862         //# Let this fail nicely.
4863         return true;
4864         //error("error opening directory %s : %s", dname, strerror(errno));
4865         //return false;
4866         }
4867     
4868     while (true)
4869         {
4870         struct dirent *de = readdir(dir);
4871         if (!de)
4872             break;
4874         //Get the directory member name
4875         String s = de->d_name;
4876         if (s.size() == 0 || s[0] == '.')
4877             continue;
4878         String childName;
4879         if (dirName.size() > 0)
4880             {
4881             childName.append(dirName);
4882             childName.append("/");
4883             }
4884         childName.append(s);
4887         struct stat finfo;
4888         String childNative = getNativePath(childName);
4889         char *cnative = (char *)childNative.c_str();
4890         if (stat(cnative, &finfo)<0)
4891             {
4892             error("cannot stat file:%s", cnative);
4893             }
4894         else if (S_ISDIR(finfo.st_mode))
4895             {
4896             //trace("DEL dir: %s", childName.c_str());
4897             if (!removeDirectory(childName))
4898                 {
4899                 return false;
4900                 }
4901             }
4902         else if (!S_ISREG(finfo.st_mode))
4903             {
4904             //trace("not regular: %s", cnative);
4905             }
4906         else
4907             {
4908             //trace("DEL file: %s", childName.c_str());
4909             if (remove(cnative)<0)
4910                 {
4911                 error("error deleting %s : %s",
4912                      cnative, strerror(errno));
4913                 return false;
4914                 }
4915             }
4916         }
4917     closedir(dir);
4919     //Now delete the directory
4920     String native = getNativePath(dirName);
4921     if (rmdir(native.c_str())<0)
4922         {
4923         error("could not delete directory %s : %s",
4924             native.c_str() , strerror(errno));
4925         return false;
4926         }
4928     return true;
4929     
4933 /**
4934  * Copy a file from one name to another. Perform only if needed
4935  */ 
4936 bool MakeBase::copyFile(const String &srcFile, const String &destFile)
4938     //# 1 Check up-to-date times
4939     String srcNative = getNativePath(srcFile);
4940     struct stat srcinfo;
4941     if (stat(srcNative.c_str(), &srcinfo)<0)
4942         {
4943         error("source file %s for copy does not exist",
4944                  srcNative.c_str());
4945         return false;
4946         }
4948     String destNative = getNativePath(destFile);
4949     struct stat destinfo;
4950     if (stat(destNative.c_str(), &destinfo)==0)
4951         {
4952         if (destinfo.st_mtime >= srcinfo.st_mtime)
4953             return true;
4954         }
4955         
4956     //# 2 prepare a destination directory if necessary
4957     unsigned int pos = destFile.find_last_of('/');
4958     if (pos != destFile.npos)
4959         {
4960         String subpath = destFile.substr(0, pos);
4961         if (!createDirectory(subpath))
4962             return false;
4963         }
4965     //# 3 do the data copy
4966 #ifndef __WIN32__
4968     FILE *srcf = fopen(srcNative.c_str(), "rb");
4969     if (!srcf)
4970         {
4971         error("copyFile cannot open '%s' for reading", srcNative.c_str());
4972         return false;
4973         }
4974     FILE *destf = fopen(destNative.c_str(), "wb");
4975     if (!destf)
4976         {
4977         error("copyFile cannot open %s for writing", srcNative.c_str());
4978         return false;
4979         }
4981     while (!feof(srcf))
4982         {
4983         int ch = fgetc(srcf);
4984         if (ch<0)
4985             break;
4986         fputc(ch, destf);
4987         }
4989     fclose(destf);
4990     fclose(srcf);
4992 #else
4993     
4994     if (!CopyFile(srcNative.c_str(), destNative.c_str(), false))
4995         {
4996         error("copyFile from %s to %s failed",
4997              srcNative.c_str(), destNative.c_str());
4998         return false;
4999         }
5000         
5001 #endif /* __WIN32__ */
5004     return true;
5009 /**
5010  * Tests if the file exists and is a regular file
5011  */ 
5012 bool MakeBase::isRegularFile(const String &fileName)
5014     String native = getNativePath(fileName);
5015     struct stat finfo;
5016     
5017     //Exists?
5018     if (stat(native.c_str(), &finfo)<0)
5019         return false;
5022     //check the file mode
5023     if (!S_ISREG(finfo.st_mode))
5024         return false;
5026     return true;
5029 /**
5030  * Tests if the file exists and is a directory
5031  */ 
5032 bool MakeBase::isDirectory(const String &fileName)
5034     String native = getNativePath(fileName);
5035     struct stat finfo;
5036     
5037     //Exists?
5038     if (stat(native.c_str(), &finfo)<0)
5039         return false;
5042     //check the file mode
5043     if (!S_ISDIR(finfo.st_mode))
5044         return false;
5046     return true;
5051 /**
5052  * Tests is the modification of fileA is newer than fileB
5053  */ 
5054 bool MakeBase::isNewerThan(const String &fileA, const String &fileB)
5056     //trace("isNewerThan:'%s' , '%s'", fileA.c_str(), fileB.c_str());
5057     String nativeA = getNativePath(fileA);
5058     struct stat infoA;
5059     //IF source does not exist, NOT newer
5060     if (stat(nativeA.c_str(), &infoA)<0)
5061         {
5062         return false;
5063         }
5065     String nativeB = getNativePath(fileB);
5066     struct stat infoB;
5067     //IF dest does not exist, YES, newer
5068     if (stat(nativeB.c_str(), &infoB)<0)
5069         {
5070         return true;
5071         }
5073     //check the actual times
5074     if (infoA.st_mtime > infoB.st_mtime)
5075         {
5076         return true;
5077         }
5079     return false;
5083 //########################################################################
5084 //# P K G    C O N F I G
5085 //########################################################################
5088 /**
5089  * Get a character from the buffer at pos.  If out of range,
5090  * return -1 for safety
5091  */
5092 int PkgConfig::get(int pos)
5094     if (pos>parselen)
5095         return -1;
5096     return parsebuf[pos];
5101 /**
5102  *  Skip over all whitespace characters beginning at pos.  Return
5103  *  the position of the first non-whitespace character.
5104  *  Pkg-config is line-oriented, so check for newline
5105  */
5106 int PkgConfig::skipwhite(int pos)
5108     while (pos < parselen)
5109         {
5110         int ch = get(pos);
5111         if (ch < 0)
5112             break;
5113         if (!isspace(ch))
5114             break;
5115         pos++;
5116         }
5117     return pos;
5121 /**
5122  *  Parse the buffer beginning at pos, for a word.  Fill
5123  *  'ret' with the result.  Return the position after the
5124  *  word.
5125  */
5126 int PkgConfig::getword(int pos, String &ret)
5128     while (pos < parselen)
5129         {
5130         int ch = get(pos);
5131         if (ch < 0)
5132             break;
5133         if (!isalnum(ch) && ch != '_' && ch != '-' && ch != '+' && ch != '.')
5134             break;
5135         ret.push_back((char)ch);
5136         pos++;
5137         }
5138     return pos;
5141 bool PkgConfig::parseRequires()
5143     if (requires.size() == 0)
5144         return true;
5145     parsebuf = (char *)requires.c_str();
5146     parselen = requires.size();
5147     int pos = 0;
5148     while (pos < parselen)
5149         {
5150         pos = skipwhite(pos);
5151         String val;
5152         int pos2 = getword(pos, val);
5153         if (pos2 == pos)
5154             break;
5155         pos = pos2;
5156         //trace("val %s", val.c_str());
5157         requireList.push_back(val);
5158         }
5159     return true;
5163 static int getint(const String str)
5165     char *s = (char *)str.c_str();
5166     char *ends = NULL;
5167     long val = strtol(s, &ends, 10);
5168     if (ends == s)
5169         return 0L;
5170     else
5171         return val;
5174 void PkgConfig::parseVersion()
5176     if (version.size() == 0)
5177         return;
5178     String s1, s2, s3;
5179     unsigned int pos = 0;
5180     unsigned int pos2 = version.find('.', pos);
5181     if (pos2 == version.npos)
5182         {
5183         s1 = version;
5184         }
5185     else
5186         {
5187         s1 = version.substr(pos, pos2-pos);
5188         pos = pos2;
5189         pos++;
5190         if (pos < version.size())
5191             {
5192             pos2 = version.find('.', pos);
5193             if (pos2 == version.npos)
5194                 {
5195                 s2 = version.substr(pos, version.size()-pos);
5196                 }
5197             else
5198                 {
5199                 s2 = version.substr(pos, pos2-pos);
5200                 pos = pos2;
5201                 pos++;
5202                 if (pos < version.size())
5203                     s3 = version.substr(pos, pos2-pos);
5204                 }
5205             }
5206         }
5208     majorVersion = getint(s1);
5209     minorVersion = getint(s2);
5210     microVersion = getint(s3);
5211     //trace("version:%d.%d.%d", majorVersion,
5212     //          minorVersion, microVersion );
5216 bool PkgConfig::parseLine(const String &lineBuf)
5218     parsebuf = (char *)lineBuf.c_str();
5219     parselen = lineBuf.size();
5220     int pos = 0;
5221     
5222     while (pos < parselen)
5223         {
5224         String attrName;
5225         pos = skipwhite(pos);
5226         int ch = get(pos);
5227         if (ch == '#')
5228             {
5229             //comment.  eat the rest of the line
5230             while (pos < parselen)
5231                 {
5232                 ch = get(pos);
5233                 if (ch == '\n' || ch < 0)
5234                     break;
5235                 pos++;
5236                 }
5237             continue;
5238             }
5239         pos = getword(pos, attrName);
5240         if (attrName.size() == 0)
5241             continue;
5242         
5243         pos = skipwhite(pos);
5244         ch = get(pos);
5245         if (ch != ':' && ch != '=')
5246             {
5247             error("expected ':' or '='");
5248             return false;
5249             }
5250         pos++;
5251         pos = skipwhite(pos);
5252         String attrVal;
5253         while (pos < parselen)
5254             {
5255             ch = get(pos);
5256             if (ch == '\n' || ch < 0)
5257                 break;
5258             else if (ch == '$' && get(pos+1) == '{')
5259                 {
5260                 //#  this is a ${substitution}
5261                 pos += 2;
5262                 String subName;
5263                 while (pos < parselen)
5264                     {
5265                     ch = get(pos);
5266                     if (ch < 0)
5267                         {
5268                         error("unterminated substitution");
5269                         return false;
5270                         }
5271                     else if (ch == '}')
5272                         break;
5273                     else
5274                         subName.push_back((char)ch);
5275                     pos++;
5276                     }
5277                 //trace("subName:%s %s", subName.c_str(), prefix.c_str());
5278                 if (subName == "prefix" && prefix.size()>0)
5279                     {
5280                     attrVal.append(prefix);
5281                     //trace("prefix override:%s", prefix.c_str());
5282                     }
5283                 else
5284                     {
5285                     String subVal = attrs[subName];
5286                     //trace("subVal:%s", subVal.c_str());
5287                     attrVal.append(subVal);
5288                     }
5289                 }
5290             else
5291                 attrVal.push_back((char)ch);
5292             pos++;
5293             }
5295         attrVal = trim(attrVal);
5296         attrs[attrName] = attrVal;
5298         String attrNameL = toLower(attrName);
5300         if (attrNameL == "name")
5301             name = attrVal;
5302         else if (attrNameL == "description")
5303             description = attrVal;
5304         else if (attrNameL == "cflags")
5305             cflags = attrVal;
5306         else if (attrNameL == "libs")
5307             libs = attrVal;
5308         else if (attrNameL == "requires")
5309             requires = attrVal;
5310         else if (attrNameL == "version")
5311             version = attrVal;
5313         //trace("name:'%s'  value:'%s'",
5314         //      attrName.c_str(), attrVal.c_str());
5315         }
5317     return true;
5321 bool PkgConfig::parse(const String &buf)
5323     init();
5325     String line;
5326     int lineNr = 0;
5327     for (unsigned int p=0 ; p<buf.size() ; p++)
5328         {
5329         int ch = buf[p];
5330         if (ch == '\n' || ch == '\r')
5331             {
5332             if (!parseLine(line))
5333                 return false;
5334             line.clear();
5335             lineNr++;
5336             }
5337         else
5338             {
5339             line.push_back(ch);
5340             }
5341         }
5342     if (line.size()>0)
5343         {
5344         if (!parseLine(line))
5345             return false;
5346         }
5348     parseRequires();
5349     parseVersion();
5351     return true;
5357 void PkgConfig::dumpAttrs()
5359     //trace("### PkgConfig attributes for %s", fileName.c_str());
5360     std::map<String, String>::iterator iter;
5361     for (iter=attrs.begin() ; iter!=attrs.end() ; iter++)
5362         {
5363         trace("   %s = %s", iter->first.c_str(), iter->second.c_str());
5364         }
5368 bool PkgConfig::readFile(const String &fname)
5370     fileName = getNativePath(fname);
5372     FILE *f = fopen(fileName.c_str(), "r");
5373     if (!f)
5374         {
5375         error("cannot open file '%s' for reading", fileName.c_str());
5376         return false;
5377         }
5378     String buf;
5379     while (true)
5380         {
5381         int ch = fgetc(f);
5382         if (ch < 0)
5383             break;
5384         buf.push_back((char)ch);
5385         }
5386     fclose(f);
5388     //trace("####### File:\n%s", buf.c_str());
5389     if (!parse(buf))
5390         {
5391         return false;
5392         }
5394     //dumpAttrs();
5396     return true;
5401 bool PkgConfig::query(const String &pkgName)
5403     name = pkgName;
5405     String fname = path;
5406     fname.append("/");
5407     fname.append(name);
5408     fname.append(".pc");
5410     if (!readFile(fname))
5411         {
5412         error("Cannot find package '%s'. Do you have it installed?",
5413                        pkgName.c_str());
5414         return false;
5415         }
5416     
5417     return true;
5424 //########################################################################
5425 //# D E P T O O L
5426 //########################################################################
5430 /**
5431  *  Class which holds information for each file.
5432  */
5433 class FileRec
5435 public:
5437     typedef enum
5438         {
5439         UNKNOWN,
5440         CFILE,
5441         HFILE,
5442         OFILE
5443         } FileType;
5445     /**
5446      *  Constructor
5447      */
5448     FileRec()
5449         { init(); type = UNKNOWN; }
5451     /**
5452      *  Copy constructor
5453      */
5454     FileRec(const FileRec &other)
5455         { init(); assign(other); }
5456     /**
5457      *  Constructor
5458      */
5459     FileRec(int typeVal)
5460         { init(); type = typeVal; }
5461     /**
5462      *  Assignment operator
5463      */
5464     FileRec &operator=(const FileRec &other)
5465         { init(); assign(other); return *this; }
5468     /**
5469      *  Destructor
5470      */
5471     ~FileRec()
5472         {}
5474     /**
5475      *  Directory part of the file name
5476      */
5477     String path;
5479     /**
5480      *  Base name, sans directory and suffix
5481      */
5482     String baseName;
5484     /**
5485      *  File extension, such as cpp or h
5486      */
5487     String suffix;
5489     /**
5490      *  Type of file: CFILE, HFILE, OFILE
5491      */
5492     int type;
5494     /**
5495      * Used to list files ref'd by this one
5496      */
5497     std::map<String, FileRec *> files;
5500 private:
5502     void init()
5503         {
5504         }
5506     void assign(const FileRec &other)
5507         {
5508         type     = other.type;
5509         baseName = other.baseName;
5510         suffix   = other.suffix;
5511         files    = other.files;
5512         }
5514 };
5518 /**
5519  *  Simpler dependency record
5520  */
5521 class DepRec
5523 public:
5525     /**
5526      *  Constructor
5527      */
5528     DepRec()
5529         {init();}
5531     /**
5532      *  Copy constructor
5533      */
5534     DepRec(const DepRec &other)
5535         {init(); assign(other);}
5536     /**
5537      *  Constructor
5538      */
5539     DepRec(const String &fname)
5540         {init(); name = fname; }
5541     /**
5542      *  Assignment operator
5543      */
5544     DepRec &operator=(const DepRec &other)
5545         {init(); assign(other); return *this;}
5548     /**
5549      *  Destructor
5550      */
5551     ~DepRec()
5552         {}
5554     /**
5555      *  Directory part of the file name
5556      */
5557     String path;
5559     /**
5560      *  Base name, without the path and suffix
5561      */
5562     String name;
5564     /**
5565      *  Suffix of the source
5566      */
5567     String suffix;
5570     /**
5571      * Used to list files ref'd by this one
5572      */
5573     std::vector<String> files;
5576 private:
5578     void init()
5579         {
5580         }
5582     void assign(const DepRec &other)
5583         {
5584         path     = other.path;
5585         name     = other.name;
5586         suffix   = other.suffix;
5587         files    = other.files; //avoid recursion
5588         }
5590 };
5593 class DepTool : public MakeBase
5595 public:
5597     /**
5598      *  Constructor
5599      */
5600     DepTool()
5601         { init(); }
5603     /**
5604      *  Copy constructor
5605      */
5606     DepTool(const DepTool &other)
5607         { init(); assign(other); }
5609     /**
5610      *  Assignment operator
5611      */
5612     DepTool &operator=(const DepTool &other)
5613         { init(); assign(other); return *this; }
5616     /**
5617      *  Destructor
5618      */
5619     ~DepTool()
5620         {}
5623     /**
5624      *  Reset this section of code
5625      */
5626     virtual void init();
5627     
5628     /**
5629      *  Reset this section of code
5630      */
5631     virtual void assign(const DepTool &other)
5632         {
5633         }
5634     
5635     /**
5636      *  Sets the source directory which will be scanned
5637      */
5638     virtual void setSourceDirectory(const String &val)
5639         { sourceDir = val; }
5641     /**
5642      *  Returns the source directory which will be scanned
5643      */
5644     virtual String getSourceDirectory()
5645         { return sourceDir; }
5647     /**
5648      *  Sets the list of files within the directory to analyze
5649      */
5650     virtual void setFileList(const std::vector<String> &list)
5651         { fileList = list; }
5653     /**
5654      * Creates the list of all file names which will be
5655      * candidates for further processing.  Reads make.exclude
5656      * to see which files for directories to leave out.
5657      */
5658     virtual bool createFileList();
5661     /**
5662      *  Generates the forward dependency list
5663      */
5664     virtual bool generateDependencies();
5667     /**
5668      *  Generates the forward dependency list, saving the file
5669      */
5670     virtual bool generateDependencies(const String &);
5673     /**
5674      *  Load a dependency file
5675      */
5676     std::vector<DepRec> loadDepFile(const String &fileName);
5678     /**
5679      *  Load a dependency file, generating one if necessary
5680      */
5681     std::vector<DepRec> getDepFile(const String &fileName,
5682               bool forceRefresh);
5684     /**
5685      *  Save a dependency file
5686      */
5687     bool saveDepFile(const String &fileName);
5690 private:
5693     /**
5694      *
5695      */
5696     void parseName(const String &fullname,
5697                    String &path,
5698                    String &basename,
5699                    String &suffix);
5701     /**
5702      *
5703      */
5704     int get(int pos);
5706     /**
5707      *
5708      */
5709     int skipwhite(int pos);
5711     /**
5712      *
5713      */
5714     int getword(int pos, String &ret);
5716     /**
5717      *
5718      */
5719     bool sequ(int pos, const char *key);
5721     /**
5722      *
5723      */
5724     bool addIncludeFile(FileRec *frec, const String &fname);
5726     /**
5727      *
5728      */
5729     bool scanFile(const String &fname, FileRec *frec);
5731     /**
5732      *
5733      */
5734     bool processDependency(FileRec *ofile, FileRec *include);
5736     /**
5737      *
5738      */
5739     String sourceDir;
5741     /**
5742      *
5743      */
5744     std::vector<String> fileList;
5746     /**
5747      *
5748      */
5749     std::vector<String> directories;
5751     /**
5752      * A list of all files which will be processed for
5753      * dependencies.
5754      */
5755     std::map<String, FileRec *> allFiles;
5757     /**
5758      * The list of .o files, and the
5759      * dependencies upon them.
5760      */
5761     std::map<String, FileRec *> oFiles;
5763     int depFileSize;
5764     char *depFileBuf;
5766     static const int readBufSize = 8192;
5767     char readBuf[8193];//byte larger
5769 };
5775 /**
5776  *  Clean up after processing.  Called by the destructor, but should
5777  *  also be called before the object is reused.
5778  */
5779 void DepTool::init()
5781     sourceDir = ".";
5783     fileList.clear();
5784     directories.clear();
5785     
5786     //clear output file list
5787     std::map<String, FileRec *>::iterator iter;
5788     for (iter=oFiles.begin(); iter!=oFiles.end() ; iter++)
5789         delete iter->second;
5790     oFiles.clear();
5792     //allFiles actually contains the master copies. delete them
5793     for (iter= allFiles.begin(); iter!=allFiles.end() ; iter++)
5794         delete iter->second;
5795     allFiles.clear(); 
5802 /**
5803  *  Parse a full path name into path, base name, and suffix
5804  */
5805 void DepTool::parseName(const String &fullname,
5806                         String &path,
5807                         String &basename,
5808                         String &suffix)
5810     if (fullname.size() < 2)
5811         return;
5813     unsigned int pos = fullname.find_last_of('/');
5814     if (pos != fullname.npos && pos<fullname.size()-1)
5815         {
5816         path = fullname.substr(0, pos);
5817         pos++;
5818         basename = fullname.substr(pos, fullname.size()-pos);
5819         }
5820     else
5821         {
5822         path = "";
5823         basename = fullname;
5824         }
5826     pos = basename.find_last_of('.');
5827     if (pos != basename.npos && pos<basename.size()-1)
5828         {
5829         suffix   = basename.substr(pos+1, basename.size()-pos-1);
5830         basename = basename.substr(0, pos);
5831         }
5833     //trace("parsename:%s %s %s", path.c_str(),
5834     //        basename.c_str(), suffix.c_str()); 
5839 /**
5840  *  Generate our internal file list.
5841  */
5842 bool DepTool::createFileList()
5845     for (unsigned int i=0 ; i<fileList.size() ; i++)
5846         {
5847         String fileName = fileList[i];
5848         //trace("## FileName:%s", fileName.c_str());
5849         String path;
5850         String basename;
5851         String sfx;
5852         parseName(fileName, path, basename, sfx);
5853         if (sfx == "cpp" || sfx == "c" || sfx == "cxx"   ||
5854             sfx == "cc" || sfx == "CC")
5855             {
5856             FileRec *fe         = new FileRec(FileRec::CFILE);
5857             fe->path            = path;
5858             fe->baseName        = basename;
5859             fe->suffix          = sfx;
5860             allFiles[fileName]  = fe;
5861             }
5862         else if (sfx == "h"   ||  sfx == "hh"  ||
5863                  sfx == "hpp" ||  sfx == "hxx")
5864             {
5865             FileRec *fe         = new FileRec(FileRec::HFILE);
5866             fe->path            = path;
5867             fe->baseName        = basename;
5868             fe->suffix          = sfx;
5869             allFiles[fileName]  = fe;
5870             }
5871         }
5873     if (!listDirectories(sourceDir, "", directories))
5874         return false;
5875         
5876     return true;
5883 /**
5884  * Get a character from the buffer at pos.  If out of range,
5885  * return -1 for safety
5886  */
5887 int DepTool::get(int pos)
5889     if (pos>depFileSize)
5890         return -1;
5891     return depFileBuf[pos];
5896 /**
5897  *  Skip over all whitespace characters beginning at pos.  Return
5898  *  the position of the first non-whitespace character.
5899  */
5900 int DepTool::skipwhite(int pos)
5902     while (pos < depFileSize)
5903         {
5904         int ch = get(pos);
5905         if (ch < 0)
5906             break;
5907         if (!isspace(ch))
5908             break;
5909         pos++;
5910         }
5911     return pos;
5915 /**
5916  *  Parse the buffer beginning at pos, for a word.  Fill
5917  *  'ret' with the result.  Return the position after the
5918  *  word.
5919  */
5920 int DepTool::getword(int pos, String &ret)
5922     while (pos < depFileSize)
5923         {
5924         int ch = get(pos);
5925         if (ch < 0)
5926             break;
5927         if (isspace(ch))
5928             break;
5929         ret.push_back((char)ch);
5930         pos++;
5931         }
5932     return pos;
5935 /**
5936  * Return whether the sequence of characters in the buffer
5937  * beginning at pos match the key,  for the length of the key
5938  */
5939 bool DepTool::sequ(int pos, const char *key)
5941     while (*key)
5942         {
5943         if (*key != get(pos))
5944             return false;
5945         key++; pos++;
5946         }
5947     return true;
5952 /**
5953  *  Add an include file name to a file record.  If the name
5954  *  is not found in allFiles explicitly, try prepending include
5955  *  directory names to it and try again.
5956  */
5957 bool DepTool::addIncludeFile(FileRec *frec, const String &iname)
5959     //# if the name is an exact match to a path name
5960     //# in allFiles, like "myinc.h"
5961     std::map<String, FileRec *>::iterator iter =
5962            allFiles.find(iname);
5963     if (iter != allFiles.end()) //already exists
5964         {
5965          //h file in same dir
5966         FileRec *other = iter->second;
5967         //trace("local: '%s'", iname.c_str());
5968         frec->files[iname] = other;
5969         return true;
5970         }
5971     else 
5972         {
5973         //## Ok, it was not found directly
5974         //look in other dirs
5975         std::vector<String>::iterator diter;
5976         for (diter=directories.begin() ;
5977              diter!=directories.end() ; diter++)
5978             {
5979             String dfname = *diter;
5980             dfname.append("/");
5981             dfname.append(iname);
5982             URI fullPathURI(dfname);  //normalize path name
5983             String fullPath = fullPathURI.getPath();
5984             if (fullPath[0] == '/')
5985                 fullPath = fullPath.substr(1);
5986             //trace("Normalized %s to %s", dfname.c_str(), fullPath.c_str());
5987             iter = allFiles.find(fullPath);
5988             if (iter != allFiles.end())
5989                 {
5990                 FileRec *other = iter->second;
5991                 //trace("other: '%s'", iname.c_str());
5992                 frec->files[fullPath] = other;
5993                 return true;
5994                 }
5995             }
5996         }
5997     return true;
6002 /**
6003  *  Lightly parse a file to find the #include directives.  Do
6004  *  a bit of state machine stuff to make sure that the directive
6005  *  is valid.  (Like not in a comment).
6006  */
6007 bool DepTool::scanFile(const String &fname, FileRec *frec)
6009     String fileName;
6010     if (sourceDir.size() > 0)
6011         {
6012         fileName.append(sourceDir);
6013         fileName.append("/");
6014         }
6015     fileName.append(fname);
6016     String nativeName = getNativePath(fileName);
6017     FILE *f = fopen(nativeName.c_str(), "r");
6018     if (!f)
6019         {
6020         error("Could not open '%s' for reading", fname.c_str());
6021         return false;
6022         }
6023     String buf;
6024     while (!feof(f))
6025         {
6026         int nrbytes = fread(readBuf, 1, readBufSize, f);
6027         readBuf[nrbytes] = '\0';
6028         buf.append(readBuf);
6029         }
6030     fclose(f);
6032     depFileSize = buf.size();
6033     depFileBuf  = (char *)buf.c_str();
6034     int pos = 0;
6037     while (pos < depFileSize)
6038         {
6039         //trace("p:%c", get(pos));
6041         //# Block comment
6042         if (get(pos) == '/' && get(pos+1) == '*')
6043             {
6044             pos += 2;
6045             while (pos < depFileSize)
6046                 {
6047                 if (get(pos) == '*' && get(pos+1) == '/')
6048                     {
6049                     pos += 2;
6050                     break;
6051                     }
6052                 else
6053                     pos++;
6054                 }
6055             }
6056         //# Line comment
6057         else if (get(pos) == '/' && get(pos+1) == '/')
6058             {
6059             pos += 2;
6060             while (pos < depFileSize)
6061                 {
6062                 if (get(pos) == '\n')
6063                     {
6064                     pos++;
6065                     break;
6066                     }
6067                 else
6068                     pos++;
6069                 }
6070             }
6071         //# #include! yaay
6072         else if (sequ(pos, "#include"))
6073             {
6074             pos += 8;
6075             pos = skipwhite(pos);
6076             String iname;
6077             pos = getword(pos, iname);
6078             if (iname.size()>2)
6079                 {
6080                 iname = iname.substr(1, iname.size()-2);
6081                 addIncludeFile(frec, iname);
6082                 }
6083             }
6084         else
6085             {
6086             pos++;
6087             }
6088         }
6090     return true;
6095 /**
6096  *  Recursively check include lists to find all files in allFiles to which
6097  *  a given file is dependent.
6098  */
6099 bool DepTool::processDependency(FileRec *ofile, FileRec *include)
6101     std::map<String, FileRec *>::iterator iter;
6102     for (iter=include->files.begin() ; iter!=include->files.end() ; iter++)
6103         {
6104         String fname  = iter->first;
6105         if (ofile->files.find(fname) != ofile->files.end())
6106             {
6107             //trace("file '%s' already seen", fname.c_str());
6108             continue;
6109             }
6110         FileRec *child  = iter->second;
6111         ofile->files[fname] = child;
6112       
6113         processDependency(ofile, child);
6114         }
6117     return true;
6124 /**
6125  *  Generate the file dependency list.
6126  */
6127 bool DepTool::generateDependencies()
6129     std::map<String, FileRec *>::iterator iter;
6130     //# First pass.  Scan for all includes
6131     for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
6132         {
6133         FileRec *frec = iter->second;
6134         if (!scanFile(iter->first, frec))
6135             {
6136             //quit?
6137             }
6138         }
6140     //# Second pass.  Scan for all includes
6141     for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
6142         {
6143         FileRec *include = iter->second;
6144         if (include->type == FileRec::CFILE)
6145             {
6146             //String cFileName   = iter->first;
6147             FileRec *ofile     = new FileRec(FileRec::OFILE);
6148             ofile->path        = include->path;
6149             ofile->baseName    = include->baseName;
6150             ofile->suffix      = include->suffix;
6151             String fname       = include->path;
6152             if (fname.size()>0)
6153                 fname.append("/");
6154             fname.append(include->baseName);
6155             fname.append(".o");
6156             oFiles[fname]    = ofile;
6157             //add the .c file first?   no, don't
6158             //ofile->files[cFileName] = include;
6159             
6160             //trace("ofile:%s", fname.c_str());
6162             processDependency(ofile, include);
6163             }
6164         }
6166       
6167     return true;
6172 /**
6173  *  High-level call to generate deps and optionally save them
6174  */
6175 bool DepTool::generateDependencies(const String &fileName)
6177     if (!createFileList())
6178         return false;
6179     if (!generateDependencies())
6180         return false;
6181     if (!saveDepFile(fileName))
6182         return false;
6183     return true;
6187 /**
6188  *   This saves the dependency cache.
6189  */
6190 bool DepTool::saveDepFile(const String &fileName)
6192     time_t tim;
6193     time(&tim);
6195     FILE *f = fopen(fileName.c_str(), "w");
6196     if (!f)
6197         {
6198         trace("cannot open '%s' for writing", fileName.c_str());
6199         }
6200     fprintf(f, "<?xml version='1.0'?>\n");
6201     fprintf(f, "<!--\n");
6202     fprintf(f, "########################################################\n");
6203     fprintf(f, "## File: build.dep\n");
6204     fprintf(f, "## Generated by BuildTool at :%s", ctime(&tim));
6205     fprintf(f, "########################################################\n");
6206     fprintf(f, "-->\n");
6208     fprintf(f, "<dependencies source='%s'>\n\n", sourceDir.c_str());
6209     std::map<String, FileRec *>::iterator iter;
6210     for (iter=oFiles.begin() ; iter!=oFiles.end() ; iter++)
6211         {
6212         FileRec *frec = iter->second;
6213         if (frec->type == FileRec::OFILE)
6214             {
6215             fprintf(f, "<object path='%s' name='%s' suffix='%s'>\n",
6216                  frec->path.c_str(), frec->baseName.c_str(), frec->suffix.c_str());
6217             std::map<String, FileRec *>::iterator citer;
6218             for (citer=frec->files.begin() ; citer!=frec->files.end() ; citer++)
6219                 {
6220                 String cfname = citer->first;
6221                 fprintf(f, "    <dep name='%s'/>\n", cfname.c_str());
6222                 }
6223             fprintf(f, "</object>\n\n");
6224             }
6225         }
6227     fprintf(f, "</dependencies>\n");
6228     fprintf(f, "\n");
6229     fprintf(f, "<!--\n");
6230     fprintf(f, "########################################################\n");
6231     fprintf(f, "## E N D\n");
6232     fprintf(f, "########################################################\n");
6233     fprintf(f, "-->\n");
6235     fclose(f);
6237     return true;
6243 /**
6244  *   This loads the dependency cache.
6245  */
6246 std::vector<DepRec> DepTool::loadDepFile(const String &depFile)
6248     std::vector<DepRec> result;
6249     
6250     Parser parser;
6251     Element *root = parser.parseFile(depFile.c_str());
6252     if (!root)
6253         {
6254         //error("Could not open %s for reading", depFile.c_str());
6255         return result;
6256         }
6258     if (root->getChildren().size()==0 ||
6259         root->getChildren()[0]->getName()!="dependencies")
6260         {
6261         error("loadDepFile: main xml element should be <dependencies>");
6262         delete root;
6263         return result;
6264         }
6266     //########## Start parsing
6267     Element *depList = root->getChildren()[0];
6269     std::vector<Element *> objects = depList->getChildren();
6270     for (unsigned int i=0 ; i<objects.size() ; i++)
6271         {
6272         Element *objectElem = objects[i];
6273         String tagName = objectElem->getName();
6274         if (tagName != "object")
6275             {
6276             error("loadDepFile: <dependencies> should have only <object> children");
6277             return result;
6278             }
6280         String objName   = objectElem->getAttribute("name");
6281          //trace("object:%s", objName.c_str());
6282         DepRec depObject(objName);
6283         depObject.path   = objectElem->getAttribute("path");
6284         depObject.suffix = objectElem->getAttribute("suffix");
6285         //########## DESCRIPTION
6286         std::vector<Element *> depElems = objectElem->getChildren();
6287         for (unsigned int i=0 ; i<depElems.size() ; i++)
6288             {
6289             Element *depElem = depElems[i];
6290             tagName = depElem->getName();
6291             if (tagName != "dep")
6292                 {
6293                 error("loadDepFile: <object> should have only <dep> children");
6294                 return result;
6295                 }
6296             String depName = depElem->getAttribute("name");
6297             //trace("    dep:%s", depName.c_str());
6298             depObject.files.push_back(depName);
6299             }
6301         //Insert into the result list, in a sorted manner
6302         bool inserted = false;
6303         std::vector<DepRec>::iterator iter;
6304         for (iter = result.begin() ; iter != result.end() ; iter++)
6305             {
6306             String vpath = iter->path;
6307             vpath.append("/");
6308             vpath.append(iter->name);
6309             String opath = depObject.path;
6310             opath.append("/");
6311             opath.append(depObject.name);
6312             if (vpath > opath)
6313                 {
6314                 inserted = true;
6315                 iter = result.insert(iter, depObject);
6316                 break;
6317                 }
6318             }
6319         if (!inserted)
6320             result.push_back(depObject);
6321         }
6323     delete root;
6325     return result;
6329 /**
6330  *   This loads the dependency cache.
6331  */
6332 std::vector<DepRec> DepTool::getDepFile(const String &depFile,
6333                    bool forceRefresh)
6335     std::vector<DepRec> result;
6336     if (forceRefresh)
6337         {
6338         generateDependencies(depFile);
6339         result = loadDepFile(depFile);
6340         }
6341     else
6342         {
6343         //try once
6344         result = loadDepFile(depFile);
6345         if (result.size() == 0)
6346             {
6347             //fail? try again
6348             generateDependencies(depFile);
6349             result = loadDepFile(depFile);
6350             }
6351         }
6352     return result;
6358 //########################################################################
6359 //# T A S K
6360 //########################################################################
6361 //forward decl
6362 class Target;
6363 class Make;
6365 /**
6366  *
6367  */
6368 class Task : public MakeBase
6371 public:
6373     typedef enum
6374         {
6375         TASK_NONE,
6376         TASK_CC,
6377         TASK_COPY,
6378         TASK_CXXTEST_PART,
6379         TASK_CXXTEST_ROOT,
6380         TASK_DELETE,
6381         TASK_ECHO,
6382         TASK_JAR,
6383         TASK_JAVAC,
6384         TASK_LINK,
6385         TASK_MAKEFILE,
6386         TASK_MKDIR,
6387         TASK_MSGFMT,
6388         TASK_PKG_CONFIG,
6389         TASK_RANLIB,
6390         TASK_RC,
6391         TASK_SHAREDLIB,
6392         TASK_STATICLIB,
6393         TASK_STRIP,
6394         TASK_TOUCH,
6395         TASK_TSTAMP
6396         } TaskType;
6397         
6399     /**
6400      *
6401      */
6402     Task(MakeBase &par) : parent(par)
6403         { init(); }
6405     /**
6406      *
6407      */
6408     Task(const Task &other) : parent(other.parent)
6409         { init(); assign(other); }
6411     /**
6412      *
6413      */
6414     Task &operator=(const Task &other)
6415         { assign(other); return *this; }
6417     /**
6418      *
6419      */
6420     virtual ~Task()
6421         { }
6424     /**
6425      *
6426      */
6427     virtual MakeBase &getParent()
6428         { return parent; }
6430      /**
6431      *
6432      */
6433     virtual int  getType()
6434         { return type; }
6436     /**
6437      *
6438      */
6439     virtual void setType(int val)
6440         { type = val; }
6442     /**
6443      *
6444      */
6445     virtual String getName()
6446         { return name; }
6448     /**
6449      *
6450      */
6451     virtual bool execute()
6452         { return true; }
6454     /**
6455      *
6456      */
6457     virtual bool parse(Element *elem)
6458         { return true; }
6460     /**
6461      *
6462      */
6463     Task *createTask(Element *elem, int lineNr);
6466 protected:
6468     void init()
6469         {
6470         type = TASK_NONE;
6471         name = "none";
6472         }
6474     void assign(const Task &other)
6475         {
6476         type = other.type;
6477         name = other.name;
6478         }
6479         
6480     /**
6481      *  Show task status
6482      */
6483     void taskstatus(const char *fmt, ...)
6484         {
6485         va_list args;
6486         va_start(args,fmt);
6487         fprintf(stdout, "    %s : ", name.c_str());
6488         vfprintf(stdout, fmt, args);
6489         fprintf(stdout, "\n");
6490         va_end(args) ;
6491         }
6493     String getAttribute(Element *elem, const String &attrName)
6494         {
6495         String str;
6496         return str;
6497         }
6499     MakeBase &parent;
6501     int type;
6503     String name;
6504 };
6508 /**
6509  * This task runs the C/C++ compiler.  The compiler is invoked
6510  * for all .c or .cpp files which are newer than their correcsponding
6511  * .o files.  
6512  */
6513 class TaskCC : public Task
6515 public:
6517     TaskCC(MakeBase &par) : Task(par)
6518         {
6519         type = TASK_CC;
6520         name = "cc";
6521         }
6523     virtual ~TaskCC()
6524         {}
6525         
6526     virtual bool isExcludedInc(const String &dirname)
6527         {
6528         for (unsigned int i=0 ; i<excludeInc.size() ; i++)
6529             {
6530             String fname = excludeInc[i];
6531             if (fname == dirname)
6532                 return true;
6533             }
6534         return false;
6535         }
6537     virtual bool execute()
6538         {
6539         //evaluate our parameters
6540         String command         = parent.eval(commandOpt, "gcc");
6541         String ccCommand       = parent.eval(ccCommandOpt, "gcc");
6542         String cxxCommand      = parent.eval(cxxCommandOpt, "g++");
6543         String source          = parent.eval(sourceOpt, ".");
6544         String dest            = parent.eval(destOpt, ".");
6545         String flags           = parent.eval(flagsOpt, "");
6546         String defines         = parent.eval(definesOpt, "");
6547         String includes        = parent.eval(includesOpt, "");
6548         bool continueOnError   = parent.evalBool(continueOnErrorOpt, true);
6549         bool refreshCache      = parent.evalBool(refreshCacheOpt, false);
6551         if (!listFiles(parent, fileSet))
6552             return false;
6553             
6554         FILE *f = NULL;
6555         f = fopen("compile.lst", "w");
6557         //refreshCache is probably false here, unless specified otherwise
6558         String fullName = parent.resolve("build.dep");
6559         if (refreshCache || isNewerThan(parent.getURI().getPath(), fullName))
6560             {
6561             taskstatus("regenerating C/C++ dependency cache");
6562             refreshCache = true;
6563             }
6565         DepTool depTool;
6566         depTool.setSourceDirectory(source);
6567         depTool.setFileList(fileSet.getFiles());
6568         std::vector<DepRec> deps =
6569              depTool.getDepFile("build.dep", refreshCache);
6570         
6571         String incs;
6572         incs.append("-I");
6573         incs.append(parent.resolve("."));
6574         incs.append(" ");
6575         if (includes.size()>0)
6576             {
6577             incs.append(includes);
6578             incs.append(" ");
6579             }
6580         std::set<String> paths;
6581         std::vector<DepRec>::iterator viter;
6582         for (viter=deps.begin() ; viter!=deps.end() ; viter++)
6583             {
6584             DepRec dep = *viter;
6585             if (dep.path.size()>0)
6586                 paths.insert(dep.path);
6587             }
6588         if (source.size()>0)
6589             {
6590             incs.append(" -I");
6591             incs.append(parent.resolve(source));
6592             incs.append(" ");
6593             }
6594         std::set<String>::iterator setIter;
6595         for (setIter=paths.begin() ; setIter!=paths.end() ; setIter++)
6596             {
6597             String dirName = *setIter;
6598             //check excludeInc to see if we dont want to include this dir
6599             if (isExcludedInc(dirName))
6600                 continue;
6601             incs.append(" -I");
6602             String dname;
6603             if (source.size()>0)
6604                 {
6605                 dname.append(source);
6606                 dname.append("/");
6607                 }
6608             dname.append(dirName);
6609             incs.append(parent.resolve(dname));
6610             }
6611             
6612         /**
6613          * Compile each of the C files that need it
6614          */
6615         bool errorOccurred = false;                 
6616         std::vector<String> cfiles;
6617         for (viter=deps.begin() ; viter!=deps.end() ; viter++)
6618             {
6619             DepRec dep = *viter;
6621             //## Select command
6622             String sfx = dep.suffix;
6623             String command = ccCommand;
6624             if (sfx == "cpp" || sfx == "cxx" || sfx == "c++" ||
6625                  sfx == "cc" || sfx == "CC")
6626                 command = cxxCommand;
6627  
6628             //## Make paths
6629             String destPath = dest;
6630             String srcPath  = source;
6631             if (dep.path.size()>0)
6632                 {
6633                 destPath.append("/");
6634                 destPath.append(dep.path);
6635                 srcPath.append("/");
6636                 srcPath.append(dep.path);
6637                 }
6638             //## Make sure destination directory exists
6639             if (!createDirectory(destPath))
6640                 return false;
6641                 
6642             //## Check whether it needs to be done
6643             String destName;
6644             if (destPath.size()>0)
6645                 {
6646                 destName.append(destPath);
6647                 destName.append("/");
6648                 }
6649             destName.append(dep.name);
6650             destName.append(".o");
6651             String destFullName = parent.resolve(destName);
6652             String srcName;
6653             if (srcPath.size()>0)
6654                 {
6655                 srcName.append(srcPath);
6656                 srcName.append("/");
6657                 }
6658             srcName.append(dep.name);
6659             srcName.append(".");
6660             srcName.append(dep.suffix);
6661             String srcFullName = parent.resolve(srcName);
6662             bool compileMe = false;
6663             //# First we check if the source is newer than the .o
6664             if (isNewerThan(srcFullName, destFullName))
6665                 {
6666                 taskstatus("compile of %s required by source: %s",
6667                         destFullName.c_str(), srcFullName.c_str());
6668                 compileMe = true;
6669                 }
6670             else
6671                 {
6672                 //# secondly, we check if any of the included dependencies
6673                 //# of the .c/.cpp is newer than the .o
6674                 for (unsigned int i=0 ; i<dep.files.size() ; i++)
6675                     {
6676                     String depName;
6677                     if (source.size()>0)
6678                         {
6679                         depName.append(source);
6680                         depName.append("/");
6681                         }
6682                     depName.append(dep.files[i]);
6683                     String depFullName = parent.resolve(depName);
6684                     bool depRequires = isNewerThan(depFullName, destFullName);
6685                     //trace("%d %s %s\n", depRequires,
6686                     //        destFullName.c_str(), depFullName.c_str());
6687                     if (depRequires)
6688                         {
6689                         taskstatus("compile of %s required by included: %s",
6690                                 destFullName.c_str(), depFullName.c_str());
6691                         compileMe = true;
6692                         break;
6693                         }
6694                     }
6695                 }
6696             if (!compileMe)
6697                 {
6698                 continue;
6699                 }
6701             //## Assemble the command
6702             String cmd = command;
6703             cmd.append(" -c ");
6704             cmd.append(flags);
6705             cmd.append(" ");
6706             cmd.append(defines);
6707             cmd.append(" ");
6708             cmd.append(incs);
6709             cmd.append(" ");
6710             cmd.append(srcFullName);
6711             cmd.append(" -o ");
6712             cmd.append(destFullName);
6714             //## Execute the command
6716             String outString, errString;
6717             bool ret = executeCommand(cmd.c_str(), "", outString, errString);
6719             if (f)
6720                 {
6721                 fprintf(f, "########################### File : %s\n",
6722                              srcFullName.c_str());
6723                 fprintf(f, "#### COMMAND ###\n");
6724                 int col = 0;
6725                 for (unsigned int i = 0 ; i < cmd.size() ; i++)
6726                     {
6727                     char ch = cmd[i];
6728                     if (isspace(ch)  && col > 63)
6729                         {
6730                         fputc('\n', f);
6731                         col = 0;
6732                         }
6733                     else
6734                         {
6735                         fputc(ch, f);
6736                         col++;
6737                         }
6738                     if (col > 76)
6739                         {
6740                         fputc('\n', f);
6741                         col = 0;
6742                         }
6743                     }
6744                 fprintf(f, "\n");
6745                 fprintf(f, "#### STDOUT ###\n%s\n", outString.c_str());
6746                 fprintf(f, "#### STDERR ###\n%s\n\n", errString.c_str());
6747                 fflush(f);
6748                 }
6749             if (!ret)
6750                 {
6751                 error("problem compiling: %s", errString.c_str());
6752                 errorOccurred = true;
6753                 }
6754             if (errorOccurred && !continueOnError)
6755                 break;
6756             }
6758         if (f)
6759             {
6760             fclose(f);
6761             }
6762         
6763         return !errorOccurred;
6764         }
6767     virtual bool parse(Element *elem)
6768         {
6769         String s;
6770         if (!parent.getAttribute(elem, "command", commandOpt))
6771             return false;
6772         if (commandOpt.size()>0)
6773             { cxxCommandOpt = ccCommandOpt = commandOpt; }
6774         if (!parent.getAttribute(elem, "cc", ccCommandOpt))
6775             return false;
6776         if (!parent.getAttribute(elem, "cxx", cxxCommandOpt))
6777             return false;
6778         if (!parent.getAttribute(elem, "destdir", destOpt))
6779             return false;
6780         if (!parent.getAttribute(elem, "continueOnError", continueOnErrorOpt))
6781             return false;
6782         if (!parent.getAttribute(elem, "refreshCache", refreshCacheOpt))
6783             return false;
6785         std::vector<Element *> children = elem->getChildren();
6786         for (unsigned int i=0 ; i<children.size() ; i++)
6787             {
6788             Element *child = children[i];
6789             String tagName = child->getName();
6790             if (tagName == "flags")
6791                 {
6792                 if (!parent.getValue(child, flagsOpt))
6793                     return false;
6794                 flagsOpt = strip(flagsOpt);
6795                 }
6796             else if (tagName == "includes")
6797                 {
6798                 if (!parent.getValue(child, includesOpt))
6799                     return false;
6800                 includesOpt = strip(includesOpt);
6801                 }
6802             else if (tagName == "defines")
6803                 {
6804                 if (!parent.getValue(child, definesOpt))
6805                     return false;
6806                 definesOpt = strip(definesOpt);
6807                 }
6808             else if (tagName == "fileset")
6809                 {
6810                 if (!parseFileSet(child, parent, fileSet))
6811                     return false;
6812                 sourceOpt = fileSet.getDirectory();
6813                 }
6814             else if (tagName == "excludeinc")
6815                 {
6816                 if (!parseFileList(child, parent, excludeInc))
6817                     return false;
6818                 }
6819             }
6821         return true;
6822         }
6823         
6824 protected:
6826     String   commandOpt;
6827     String   ccCommandOpt;
6828     String   cxxCommandOpt;
6829     String   sourceOpt;
6830     String   destOpt;
6831     String   flagsOpt;
6832     String   definesOpt;
6833     String   includesOpt;
6834     String   continueOnErrorOpt;
6835     String   refreshCacheOpt;
6836     FileSet  fileSet;
6837     FileList excludeInc;
6838     
6839 };
6843 /**
6844  *
6845  */
6846 class TaskCopy : public Task
6848 public:
6850     typedef enum
6851         {
6852         CP_NONE,
6853         CP_TOFILE,
6854         CP_TODIR
6855         } CopyType;
6857     TaskCopy(MakeBase &par) : Task(par)
6858         {
6859         type        = TASK_COPY;
6860         name        = "copy";
6861         cptype      = CP_NONE;
6862         haveFileSet = false;
6863         }
6865     virtual ~TaskCopy()
6866         {}
6868     virtual bool execute()
6869         {
6870         String fileName   = parent.eval(fileNameOpt   , ".");
6871         String toFileName = parent.eval(toFileNameOpt , ".");
6872         String toDirName  = parent.eval(toDirNameOpt  , ".");
6873         bool   verbose    = parent.evalBool(verboseOpt, false);
6874         switch (cptype)
6875            {
6876            case CP_TOFILE:
6877                {
6878                if (fileName.size()>0)
6879                    {
6880                    taskstatus("%s to %s",
6881                         fileName.c_str(), toFileName.c_str());
6882                    String fullSource = parent.resolve(fileName);
6883                    String fullDest = parent.resolve(toFileName);
6884                    if (verbose)
6885                        taskstatus("copy %s to file %s", fullSource.c_str(),
6886                                           fullDest.c_str());
6887                    if (!isRegularFile(fullSource))
6888                        {
6889                        error("copy : file %s does not exist", fullSource.c_str());
6890                        return false;
6891                        }
6892                    if (!isNewerThan(fullSource, fullDest))
6893                        {
6894                        taskstatus("skipped");
6895                        return true;
6896                        }
6897                    if (!copyFile(fullSource, fullDest))
6898                        return false;
6899                    taskstatus("1 file copied");
6900                    }
6901                return true;
6902                }
6903            case CP_TODIR:
6904                {
6905                if (haveFileSet)
6906                    {
6907                    if (!listFiles(parent, fileSet))
6908                        return false;
6909                    String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
6911                    taskstatus("%s to %s",
6912                        fileSetDir.c_str(), toDirName.c_str());
6914                    int nrFiles = 0;
6915                    for (unsigned int i=0 ; i<fileSet.size() ; i++)
6916                        {
6917                        String fileName = fileSet[i];
6919                        String sourcePath;
6920                        if (fileSetDir.size()>0)
6921                            {
6922                            sourcePath.append(fileSetDir);
6923                            sourcePath.append("/");
6924                            }
6925                        sourcePath.append(fileName);
6926                        String fullSource = parent.resolve(sourcePath);
6927                        
6928                        //Get the immediate parent directory's base name
6929                        String baseFileSetDir = fileSetDir;
6930                        unsigned int pos = baseFileSetDir.find_last_of('/');
6931                        if (pos!=baseFileSetDir.npos &&
6932                                   pos < baseFileSetDir.size()-1)
6933                            baseFileSetDir =
6934                               baseFileSetDir.substr(pos+1,
6935                                    baseFileSetDir.size());
6936                        //Now make the new path
6937                        String destPath;
6938                        if (toDirName.size()>0)
6939                            {
6940                            destPath.append(toDirName);
6941                            destPath.append("/");
6942                            }
6943                        if (baseFileSetDir.size()>0)
6944                            {
6945                            destPath.append(baseFileSetDir);
6946                            destPath.append("/");
6947                            }
6948                        destPath.append(fileName);
6949                        String fullDest = parent.resolve(destPath);
6950                        //trace("fileName:%s", fileName.c_str());
6951                        if (verbose)
6952                            taskstatus("copy %s to new dir : %s",
6953                                  fullSource.c_str(), fullDest.c_str());
6954                        if (!isNewerThan(fullSource, fullDest))
6955                            {
6956                            if (verbose)
6957                                taskstatus("copy skipping %s", fullSource.c_str());
6958                            continue;
6959                            }
6960                        if (!copyFile(fullSource, fullDest))
6961                            return false;
6962                        nrFiles++;
6963                        }
6964                    taskstatus("%d file(s) copied", nrFiles);
6965                    }
6966                else //file source
6967                    {
6968                    //For file->dir we want only the basename of
6969                    //the source appended to the dest dir
6970                    taskstatus("%s to %s", 
6971                        fileName.c_str(), toDirName.c_str());
6972                    String baseName = fileName;
6973                    unsigned int pos = baseName.find_last_of('/');
6974                    if (pos!=baseName.npos && pos<baseName.size()-1)
6975                        baseName = baseName.substr(pos+1, baseName.size());
6976                    String fullSource = parent.resolve(fileName);
6977                    String destPath;
6978                    if (toDirName.size()>0)
6979                        {
6980                        destPath.append(toDirName);
6981                        destPath.append("/");
6982                        }
6983                    destPath.append(baseName);
6984                    String fullDest = parent.resolve(destPath);
6985                    if (verbose)
6986                        taskstatus("file %s to new dir : %s", fullSource.c_str(),
6987                                           fullDest.c_str());
6988                    if (!isRegularFile(fullSource))
6989                        {
6990                        error("copy : file %s does not exist", fullSource.c_str());
6991                        return false;
6992                        }
6993                    if (!isNewerThan(fullSource, fullDest))
6994                        {
6995                        taskstatus("skipped");
6996                        return true;
6997                        }
6998                    if (!copyFile(fullSource, fullDest))
6999                        return false;
7000                    taskstatus("1 file copied");
7001                    }
7002                return true;
7003                }
7004            }
7005         return true;
7006         }
7009     virtual bool parse(Element *elem)
7010         {
7011         if (!parent.getAttribute(elem, "file", fileNameOpt))
7012             return false;
7013         if (!parent.getAttribute(elem, "tofile", toFileNameOpt))
7014             return false;
7015         if (toFileNameOpt.size() > 0)
7016             cptype = CP_TOFILE;
7017         if (!parent.getAttribute(elem, "todir", toDirNameOpt))
7018             return false;
7019         if (toDirNameOpt.size() > 0)
7020             cptype = CP_TODIR;
7021         if (!parent.getAttribute(elem, "verbose", verboseOpt))
7022             return false;
7023             
7024         haveFileSet = false;
7025         
7026         std::vector<Element *> children = elem->getChildren();
7027         for (unsigned int i=0 ; i<children.size() ; i++)
7028             {
7029             Element *child = children[i];
7030             String tagName = child->getName();
7031             if (tagName == "fileset")
7032                 {
7033                 if (!parseFileSet(child, parent, fileSet))
7034                     {
7035                     error("problem getting fileset");
7036                     return false;
7037                     }
7038                 haveFileSet = true;
7039                 }
7040             }
7042         //Perform validity checks
7043         if (fileNameOpt.size()>0 && fileSet.size()>0)
7044             {
7045             error("<copy> can only have one of : file= and <fileset>");
7046             return false;
7047             }
7048         if (toFileNameOpt.size()>0 && toDirNameOpt.size()>0)
7049             {
7050             error("<copy> can only have one of : tofile= or todir=");
7051             return false;
7052             }
7053         if (haveFileSet && toDirNameOpt.size()==0)
7054             {
7055             error("a <copy> task with a <fileset> must have : todir=");
7056             return false;
7057             }
7058         if (cptype == CP_TOFILE && fileNameOpt.size()==0)
7059             {
7060             error("<copy> tofile= must be associated with : file=");
7061             return false;
7062             }
7063         if (cptype == CP_TODIR && fileNameOpt.size()==0 && !haveFileSet)
7064             {
7065             error("<copy> todir= must be associated with : file= or <fileset>");
7066             return false;
7067             }
7069         return true;
7070         }
7071         
7072 private:
7074     int cptype;
7075     bool haveFileSet;
7077     FileSet fileSet;
7078     String  fileNameOpt;
7079     String  toFileNameOpt;
7080     String  toDirNameOpt;
7081     String  verboseOpt;
7082 };
7085 /**
7086  * Generate CxxTest files
7087  */
7088 class TaskCxxTestPart: public Task
7090 public:
7092     TaskCxxTestPart(MakeBase &par) : Task(par)
7093          {
7094          type    = TASK_CXXTEST_PART;
7095          name    = "cxxtestpart";
7096          }
7098     virtual ~TaskCxxTestPart()
7099         {}
7101     virtual bool execute()
7102         {
7103         if (!listFiles(parent, fileSet))
7104             return false;
7105         String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
7106                 
7107         String fullDest = parent.resolve(parent.eval(destPathOpt, "."));
7108         String cmd = parent.eval(commandOpt, "cxxtestgen.py");
7109         cmd.append(" --part -o ");
7110         cmd.append(fullDest);
7112         unsigned int newFiles = 0;
7113         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7114             {
7115             String fileName = fileSet[i];
7116             if (getSuffix(fileName) != "h")
7117                 continue;
7118             String sourcePath;
7119             if (fileSetDir.size()>0)
7120                 {
7121                 sourcePath.append(fileSetDir);
7122                 sourcePath.append("/");
7123                 }
7124             sourcePath.append(fileName);
7125             String fullSource = parent.resolve(sourcePath);
7127             cmd.append(" ");
7128             cmd.append(fullSource);
7129             if (isNewerThan(fullSource, fullDest)) newFiles++;
7130             }
7131         
7132         if (newFiles>0) {
7133             size_t const lastSlash = fullDest.find_last_of('/');
7134             if (lastSlash != fullDest.npos) {
7135                 String directory(fullDest, 0, lastSlash);
7136                 if (!createDirectory(directory))
7137                     return false;
7138             }
7140             String outString, errString;
7141             if (!executeCommand(cmd.c_str(), "", outString, errString))
7142                 {
7143                 error("<cxxtestpart> problem: %s", errString.c_str());
7144                 return false;
7145                 }
7146         }
7148         return true;
7149         }
7151     virtual bool parse(Element *elem)
7152         {
7153         if (!parent.getAttribute(elem, "command", commandOpt))
7154             return false;
7155         if (!parent.getAttribute(elem, "out", destPathOpt))
7156             return false;
7157             
7158         std::vector<Element *> children = elem->getChildren();
7159         for (unsigned int i=0 ; i<children.size() ; i++)
7160             {
7161             Element *child = children[i];
7162             String tagName = child->getName();
7163             if (tagName == "fileset")
7164                 {
7165                 if (!parseFileSet(child, parent, fileSet))
7166                     return false;
7167                 }
7168             }
7169         return true;
7170         }
7172 private:
7174     String  commandOpt;
7175     String  destPathOpt;
7176     FileSet fileSet;
7178 };
7181 /**
7182  * Generate the CxxTest root file
7183  */
7184 class TaskCxxTestRoot: public Task
7186 public:
7188     TaskCxxTestRoot(MakeBase &par) : Task(par)
7189          {
7190          type    = TASK_CXXTEST_ROOT;
7191          name    = "cxxtestroot";
7192          }
7194     virtual ~TaskCxxTestRoot()
7195         {}
7197     virtual bool execute()
7198         {
7199         if (!listFiles(parent, fileSet))
7200             return false;
7201         String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
7202         unsigned int newFiles = 0;
7203                 
7204         String fullDest = parent.resolve(parent.eval(destPathOpt, "."));
7205         String cmd = parent.eval(commandOpt, "cxxtestgen.py");
7206         cmd.append(" --root -o ");
7207         cmd.append(fullDest);
7208         String templateFile = parent.eval(templateFileOpt, "");
7209         if (templateFile.size()>0) {
7210             String fullTemplate = parent.resolve(templateFile);
7211             cmd.append(" --template=");
7212             cmd.append(fullTemplate);
7213             if (isNewerThan(fullTemplate, fullDest)) newFiles++;
7214         }
7216         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7217             {
7218             String fileName = fileSet[i];
7219             if (getSuffix(fileName) != "h")
7220                 continue;
7221             String sourcePath;
7222             if (fileSetDir.size()>0)
7223                 {
7224                 sourcePath.append(fileSetDir);
7225                 sourcePath.append("/");
7226                 }
7227             sourcePath.append(fileName);
7228             String fullSource = parent.resolve(sourcePath);
7230             cmd.append(" ");
7231             cmd.append(fullSource);
7232             if (isNewerThan(fullSource, fullDest)) newFiles++;
7233             }
7234         
7235         if (newFiles>0) {
7236             size_t const lastSlash = fullDest.find_last_of('/');
7237             if (lastSlash != fullDest.npos) {
7238                 String directory(fullDest, 0, lastSlash);
7239                 if (!createDirectory(directory))
7240                     return false;
7241             }
7243             String outString, errString;
7244             if (!executeCommand(cmd.c_str(), "", outString, errString))
7245                 {
7246                 error("<cxxtestroot> problem: %s", errString.c_str());
7247                 return false;
7248                 }
7249         }
7251         return true;
7252         }
7254     virtual bool parse(Element *elem)
7255         {
7256         if (!parent.getAttribute(elem, "command", commandOpt))
7257             return false;
7258         if (!parent.getAttribute(elem, "template", templateFileOpt))
7259             return false;
7260         if (!parent.getAttribute(elem, "out", destPathOpt))
7261             return false;
7262             
7263         std::vector<Element *> children = elem->getChildren();
7264         for (unsigned int i=0 ; i<children.size() ; i++)
7265             {
7266             Element *child = children[i];
7267             String tagName = child->getName();
7268             if (tagName == "fileset")
7269                 {
7270                 if (!parseFileSet(child, parent, fileSet))
7271                     return false;
7272                 }
7273             }
7274         return true;
7275         }
7277 private:
7279     String  commandOpt;
7280     String  templateFileOpt;
7281     String  destPathOpt;
7282     FileSet fileSet;
7284 };
7287 /**
7288  *
7289  */
7290 class TaskDelete : public Task
7292 public:
7294     typedef enum
7295         {
7296         DEL_FILE,
7297         DEL_DIR,
7298         DEL_FILESET
7299         } DeleteType;
7301     TaskDelete(MakeBase &par) : Task(par)
7302         { 
7303         type        = TASK_DELETE;
7304         name        = "delete";
7305         delType     = DEL_FILE;
7306         }
7308     virtual ~TaskDelete()
7309         {}
7311     virtual bool execute()
7312         {
7313         String dirName   = parent.eval(dirNameOpt, ".");
7314         String fileName  = parent.eval(fileNameOpt, ".");
7315         bool verbose     = parent.evalBool(verboseOpt, false);
7316         bool quiet       = parent.evalBool(quietOpt, false);
7317         bool failOnError = parent.evalBool(failOnErrorOpt, true);
7318         struct stat finfo;
7319         switch (delType)
7320             {
7321             case DEL_FILE:
7322                 {
7323                 taskstatus("file: %s", fileName.c_str());
7324                 String fullName = parent.resolve(fileName);
7325                 char *fname = (char *)fullName.c_str();
7326                 if (!quiet && verbose)
7327                     taskstatus("path: %s", fname);
7328                 //does not exist
7329                 if (stat(fname, &finfo)<0)
7330                     {
7331                     if (failOnError)
7332                         return false;
7333                     else
7334                         return true;
7335                     }
7336                 //exists but is not a regular file
7337                 if (!S_ISREG(finfo.st_mode))
7338                     {
7339                     error("<delete> failed. '%s' exists and is not a regular file",
7340                           fname);
7341                     return false;
7342                     }
7343                 if (remove(fname)<0)
7344                     {
7345                     error("<delete> failed: %s", strerror(errno));
7346                     return false;
7347                     }
7348                 return true;
7349                 }
7350             case DEL_DIR:
7351                 {
7352                 taskstatus("dir: %s", dirName.c_str());
7353                 String fullDir = parent.resolve(dirName);
7354                 if (!quiet && verbose)
7355                     taskstatus("path: %s", fullDir.c_str());
7356                 if (!removeDirectory(fullDir))
7357                     return false;
7358                 return true;
7359                 }
7360             }
7361         return true;
7362         }
7364     virtual bool parse(Element *elem)
7365         {
7366         if (!parent.getAttribute(elem, "file", fileNameOpt))
7367             return false;
7368         if (fileNameOpt.size() > 0)
7369             delType = DEL_FILE;
7370         if (!parent.getAttribute(elem, "dir", dirNameOpt))
7371             return false;
7372         if (dirNameOpt.size() > 0)
7373             delType = DEL_DIR;
7374         if (fileNameOpt.size()>0 && dirNameOpt.size()>0)
7375             {
7376             error("<delete> can have one attribute of file= or dir=");
7377             return false;
7378             }
7379         if (fileNameOpt.size()==0 && dirNameOpt.size()==0)
7380             {
7381             error("<delete> must have one attribute of file= or dir=");
7382             return false;
7383             }
7384         if (!parent.getAttribute(elem, "verbose", verboseOpt))
7385             return false;
7386         if (!parent.getAttribute(elem, "quiet", quietOpt))
7387             return false;
7388         if (!parent.getAttribute(elem, "failonerror", failOnErrorOpt))
7389             return false;
7390         return true;
7391         }
7393 private:
7395     int delType;
7396     String dirNameOpt;
7397     String fileNameOpt;
7398     String verboseOpt;
7399     String quietOpt;
7400     String failOnErrorOpt;
7401 };
7404 /**
7405  * Send a message to stdout
7406  */
7407 class TaskEcho : public Task
7409 public:
7411     TaskEcho(MakeBase &par) : Task(par)
7412         { type = TASK_ECHO; name = "echo"; }
7414     virtual ~TaskEcho()
7415         {}
7417     virtual bool execute()
7418         {
7419         //let message have priority over text
7420         String message = parent.eval(messageOpt, "");
7421         String text    = parent.eval(textOpt, "");
7422         if (message.size() > 0)
7423             {
7424             fprintf(stdout, "%s\n", message.c_str());
7425             }
7426         else if (text.size() > 0)
7427             {
7428             fprintf(stdout, "%s\n", text.c_str());
7429             }
7430         return true;
7431         }
7433     virtual bool parse(Element *elem)
7434         {
7435         if (!parent.getValue(elem, textOpt))
7436             return false;
7437         textOpt    = leftJustify(textOpt);
7438         if (!parent.getAttribute(elem, "message", messageOpt))
7439             return false;
7440         return true;
7441         }
7443 private:
7445     String messageOpt;
7446     String textOpt;
7447 };
7451 /**
7452  *
7453  */
7454 class TaskJar : public Task
7456 public:
7458     TaskJar(MakeBase &par) : Task(par)
7459         { type = TASK_JAR; name = "jar"; }
7461     virtual ~TaskJar()
7462         {}
7464     virtual bool execute()
7465         {
7466         String command  = parent.eval(commandOpt, "jar");
7467         String basedir  = parent.eval(basedirOpt, ".");
7468         String destfile = parent.eval(destfileOpt, ".");
7470         String cmd = command;
7471         cmd.append(" -cf ");
7472         cmd.append(destfile);
7473         cmd.append(" -C ");
7474         cmd.append(basedir);
7475         cmd.append(" .");
7477         String execCmd = cmd;
7479         String outString, errString;
7480         bool ret = executeCommand(execCmd.c_str(), "", outString, errString);
7481         if (!ret)
7482             {
7483             error("<jar> command '%s' failed :\n %s",
7484                                       execCmd.c_str(), errString.c_str());
7485             return false;
7486             }
7487         return true;
7488         }
7490     virtual bool parse(Element *elem)
7491         {
7492         if (!parent.getAttribute(elem, "command", commandOpt))
7493             return false;
7494         if (!parent.getAttribute(elem, "basedir", basedirOpt))
7495             return false;
7496         if (!parent.getAttribute(elem, "destfile", destfileOpt))
7497             return false;
7498         if (basedirOpt.size() == 0 || destfileOpt.size() == 0)
7499             {
7500             error("<jar> required both basedir and destfile attributes to be set");
7501             return false;
7502             }
7503         return true;
7504         }
7506 private:
7508     String commandOpt;
7509     String basedirOpt;
7510     String destfileOpt;
7511 };
7514 /**
7515  *
7516  */
7517 class TaskJavac : public Task
7519 public:
7521     TaskJavac(MakeBase &par) : Task(par)
7522         { 
7523         type = TASK_JAVAC; name = "javac";
7524         }
7526     virtual ~TaskJavac()
7527         {}
7529     virtual bool execute()
7530         {
7531         String command  = parent.eval(commandOpt, "javac");
7532         String srcdir   = parent.eval(srcdirOpt, ".");
7533         String destdir  = parent.eval(destdirOpt, ".");
7534         String target   = parent.eval(targetOpt, "");
7536         std::vector<String> fileList;
7537         if (!listFiles(srcdir, "", fileList))
7538             {
7539             return false;
7540             }
7541         String cmd = command;
7542         cmd.append(" -d ");
7543         cmd.append(destdir);
7544         cmd.append(" -classpath ");
7545         cmd.append(destdir);
7546         cmd.append(" -sourcepath ");
7547         cmd.append(srcdir);
7548         cmd.append(" ");
7549         if (target.size()>0)
7550             {
7551             cmd.append(" -target ");
7552             cmd.append(target);
7553             cmd.append(" ");
7554             }
7555         String fname = "javalist.btool";
7556         FILE *f = fopen(fname.c_str(), "w");
7557         int count = 0;
7558         for (unsigned int i=0 ; i<fileList.size() ; i++)
7559             {
7560             String fname = fileList[i];
7561             String srcName = fname;
7562             if (fname.size()<6) //x.java
7563                 continue;
7564             if (fname.compare(fname.size()-5, 5, ".java") != 0)
7565                 continue;
7566             String baseName = fname.substr(0, fname.size()-5);
7567             String destName = baseName;
7568             destName.append(".class");
7570             String fullSrc = srcdir;
7571             fullSrc.append("/");
7572             fullSrc.append(fname);
7573             String fullDest = destdir;
7574             fullDest.append("/");
7575             fullDest.append(destName);
7576             //trace("fullsrc:%s fulldest:%s", fullSrc.c_str(), fullDest.c_str());
7577             if (!isNewerThan(fullSrc, fullDest))
7578                 continue;
7580             count++;
7581             fprintf(f, "%s\n", fullSrc.c_str());
7582             }
7583         fclose(f);
7584         if (!count)
7585             {
7586             taskstatus("nothing to do");
7587             return true;
7588             }
7590         taskstatus("compiling %d files", count);
7592         String execCmd = cmd;
7593         execCmd.append("@");
7594         execCmd.append(fname);
7596         String outString, errString;
7597         bool ret = executeCommand(execCmd.c_str(), "", outString, errString);
7598         if (!ret)
7599             {
7600             error("<javac> command '%s' failed :\n %s",
7601                                       execCmd.c_str(), errString.c_str());
7602             return false;
7603             }
7604         return true;
7605         }
7607     virtual bool parse(Element *elem)
7608         {
7609         if (!parent.getAttribute(elem, "command", commandOpt))
7610             return false;
7611         if (!parent.getAttribute(elem, "srcdir", srcdirOpt))
7612             return false;
7613         if (!parent.getAttribute(elem, "destdir", destdirOpt))
7614             return false;
7615         if (srcdirOpt.size() == 0 || destdirOpt.size() == 0)
7616             {
7617             error("<javac> required both srcdir and destdir attributes to be set");
7618             return false;
7619             }
7620         if (!parent.getAttribute(elem, "target", targetOpt))
7621             return false;
7622         return true;
7623         }
7625 private:
7627     String commandOpt;
7628     String srcdirOpt;
7629     String destdirOpt;
7630     String targetOpt;
7632 };
7635 /**
7636  *
7637  */
7638 class TaskLink : public Task
7640 public:
7642     TaskLink(MakeBase &par) : Task(par)
7643         {
7644         type = TASK_LINK; name = "link";
7645         }
7647     virtual ~TaskLink()
7648         {}
7650     virtual bool execute()
7651         {
7652         String  command        = parent.eval(commandOpt, "g++");
7653         String  fileName       = parent.eval(fileNameOpt, "");
7654         String  flags          = parent.eval(flagsOpt, "");
7655         String  libs           = parent.eval(libsOpt, "");
7656         bool    doStrip        = parent.evalBool(doStripOpt, false);
7657         String  symFileName    = parent.eval(symFileNameOpt, "");
7658         String  stripCommand   = parent.eval(stripCommandOpt, "strip");
7659         String  objcopyCommand = parent.eval(objcopyCommandOpt, "objcopy");
7661         if (!listFiles(parent, fileSet))
7662             return false;
7663         String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
7664         //trace("%d files in %s", fileSet.size(), fileSetDir.c_str());
7665         bool doit = false;
7666         String fullTarget = parent.resolve(fileName);
7667         String cmd = command;
7668         cmd.append(" -o ");
7669         cmd.append(fullTarget);
7670         cmd.append(" ");
7671         cmd.append(flags);
7672         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7673             {
7674             cmd.append(" ");
7675             String obj;
7676             if (fileSetDir.size()>0)
7677                 {
7678                 obj.append(fileSetDir);
7679                 obj.append("/");
7680                 }
7681             obj.append(fileSet[i]);
7682             String fullObj = parent.resolve(obj);
7683             String nativeFullObj = getNativePath(fullObj);
7684             cmd.append(nativeFullObj);
7685             //trace("link: tgt:%s obj:%s", fullTarget.c_str(),
7686             //          fullObj.c_str());
7687             if (isNewerThan(fullObj, fullTarget))
7688                 doit = true;
7689             }
7690         cmd.append(" ");
7691         cmd.append(libs);
7692         if (!doit)
7693             {
7694             //trace("link not needed");
7695             return true;
7696             }
7697         //trace("LINK cmd:%s", cmd.c_str());
7700         String outbuf, errbuf;
7701         if (!executeCommand(cmd.c_str(), "", outbuf, errbuf))
7702             {
7703             error("LINK problem: %s", errbuf.c_str());
7704             return false;
7705             }
7707         if (symFileName.size()>0)
7708             {
7709             String symFullName = parent.resolve(symFileName);
7710             cmd = objcopyCommand;
7711             cmd.append(" --only-keep-debug ");
7712             cmd.append(getNativePath(fullTarget));
7713             cmd.append(" ");
7714             cmd.append(getNativePath(symFullName));
7715             if (!executeCommand(cmd, "", outbuf, errbuf))
7716                 {
7717                 error("<strip> symbol file failed : %s", errbuf.c_str());
7718                 return false;
7719                 }
7720             }
7721             
7722         if (doStrip)
7723             {
7724             cmd = stripCommand;
7725             cmd.append(" ");
7726             cmd.append(getNativePath(fullTarget));
7727             if (!executeCommand(cmd, "", outbuf, errbuf))
7728                {
7729                error("<strip> failed : %s", errbuf.c_str());
7730                return false;
7731                }
7732             }
7734         return true;
7735         }
7737     virtual bool parse(Element *elem)
7738         {
7739         if (!parent.getAttribute(elem, "command", commandOpt))
7740             return false;
7741         if (!parent.getAttribute(elem, "objcopycommand", objcopyCommandOpt))
7742             return false;
7743         if (!parent.getAttribute(elem, "stripcommand", stripCommandOpt))
7744             return false;
7745         if (!parent.getAttribute(elem, "out", fileNameOpt))
7746             return false;
7747         if (!parent.getAttribute(elem, "strip", doStripOpt))
7748             return false;
7749         if (!parent.getAttribute(elem, "symfile", symFileNameOpt))
7750             return false;
7751             
7752         std::vector<Element *> children = elem->getChildren();
7753         for (unsigned int i=0 ; i<children.size() ; i++)
7754             {
7755             Element *child = children[i];
7756             String tagName = child->getName();
7757             if (tagName == "fileset")
7758                 {
7759                 if (!parseFileSet(child, parent, fileSet))
7760                     return false;
7761                 }
7762             else if (tagName == "flags")
7763                 {
7764                 if (!parent.getValue(child, flagsOpt))
7765                     return false;
7766                 flagsOpt = strip(flagsOpt);
7767                 }
7768             else if (tagName == "libs")
7769                 {
7770                 if (!parent.getValue(child, libsOpt))
7771                     return false;
7772                 libsOpt = strip(libsOpt);
7773                 }
7774             }
7775         return true;
7776         }
7778 private:
7780     FileSet fileSet;
7782     String  commandOpt;
7783     String  fileNameOpt;
7784     String  flagsOpt;
7785     String  libsOpt;
7786     String  doStripOpt;
7787     String  symFileNameOpt;
7788     String  stripCommandOpt;
7789     String  objcopyCommandOpt;
7791 };
7795 /**
7796  * Create a named file
7797  */
7798 class TaskMakeFile : public Task
7800 public:
7802     TaskMakeFile(MakeBase &par) : Task(par)
7803         { type = TASK_MAKEFILE; name = "makefile"; }
7805     virtual ~TaskMakeFile()
7806         {}
7808     virtual bool execute()
7809         {
7810         String fileName = parent.eval(fileNameOpt, "");
7811         String text     = parent.eval(textOpt, "");
7813         taskstatus("%s", fileName.c_str());
7814         String fullName = parent.resolve(fileName);
7815         if (!isNewerThan(parent.getURI().getPath(), fullName))
7816             {
7817             //trace("skipped <makefile>");
7818             return true;
7819             }
7820         String fullNative = getNativePath(fullName);
7821         //trace("fullName:%s", fullName.c_str());
7822         FILE *f = fopen(fullNative.c_str(), "w");
7823         if (!f)
7824             {
7825             error("<makefile> could not open %s for writing : %s",
7826                 fullName.c_str(), strerror(errno));
7827             return false;
7828             }
7829         for (unsigned int i=0 ; i<text.size() ; i++)
7830             fputc(text[i], f);
7831         fputc('\n', f);
7832         fclose(f);
7833         return true;
7834         }
7836     virtual bool parse(Element *elem)
7837         {
7838         if (!parent.getAttribute(elem, "file", fileNameOpt))
7839             return false;
7840         if (fileNameOpt.size() == 0)
7841             {
7842             error("<makefile> requires 'file=\"filename\"' attribute");
7843             return false;
7844             }
7845         if (!parent.getValue(elem, textOpt))
7846             return false;
7847         textOpt = leftJustify(textOpt);
7848         //trace("dirname:%s", dirName.c_str());
7849         return true;
7850         }
7852 private:
7854     String fileNameOpt;
7855     String textOpt;
7856 };
7860 /**
7861  * Create a named directory
7862  */
7863 class TaskMkDir : public Task
7865 public:
7867     TaskMkDir(MakeBase &par) : Task(par)
7868         { type = TASK_MKDIR; name = "mkdir"; }
7870     virtual ~TaskMkDir()
7871         {}
7873     virtual bool execute()
7874         {
7875         String dirName = parent.eval(dirNameOpt, ".");
7876         
7877         taskstatus("%s", dirName.c_str());
7878         String fullDir = parent.resolve(dirName);
7879         //trace("fullDir:%s", fullDir.c_str());
7880         if (!createDirectory(fullDir))
7881             return false;
7882         return true;
7883         }
7885     virtual bool parse(Element *elem)
7886         {
7887         if (!parent.getAttribute(elem, "dir", dirNameOpt))
7888             return false;
7889         if (dirNameOpt.size() == 0)
7890             {
7891             error("<mkdir> requires 'dir=\"dirname\"' attribute");
7892             return false;
7893             }
7894         return true;
7895         }
7897 private:
7899     String dirNameOpt;
7900 };
7904 /**
7905  * Create a named directory
7906  */
7907 class TaskMsgFmt: public Task
7909 public:
7911     TaskMsgFmt(MakeBase &par) : Task(par)
7912          { type = TASK_MSGFMT;  name = "msgfmt"; }
7914     virtual ~TaskMsgFmt()
7915         {}
7917     virtual bool execute()
7918         {
7919         String  command   = parent.eval(commandOpt, "msgfmt");
7920         String  toDirName = parent.eval(toDirNameOpt, ".");
7921         String  outName   = parent.eval(outNameOpt, "");
7922         bool    owndir    = parent.evalBool(owndirOpt, false);
7924         if (!listFiles(parent, fileSet))
7925             return false;
7926         String fileSetDir = fileSet.getDirectory();
7928         //trace("msgfmt: %d", fileSet.size());
7929         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7930             {
7931             String fileName = fileSet[i];
7932             if (getSuffix(fileName) != "po")
7933                 continue;
7934             String sourcePath;
7935             if (fileSetDir.size()>0)
7936                 {
7937                 sourcePath.append(fileSetDir);
7938                 sourcePath.append("/");
7939                 }
7940             sourcePath.append(fileName);
7941             String fullSource = parent.resolve(sourcePath);
7943             String destPath;
7944             if (toDirName.size()>0)
7945                 {
7946                 destPath.append(toDirName);
7947                 destPath.append("/");
7948                 }
7949             if (owndir)
7950                 {
7951                 String subdir = fileName;
7952                 unsigned int pos = subdir.find_last_of('.');
7953                 if (pos != subdir.npos)
7954                     subdir = subdir.substr(0, pos);
7955                 destPath.append(subdir);
7956                 destPath.append("/");
7957                 }
7958             //Pick the output file name
7959             if (outName.size() > 0)
7960                 {
7961                 destPath.append(outName);
7962                 }
7963             else
7964                 {
7965                 destPath.append(fileName);
7966                 destPath[destPath.size()-2] = 'm';
7967                 }
7969             String fullDest = parent.resolve(destPath);
7971             if (!isNewerThan(fullSource, fullDest))
7972                 {
7973                 //trace("skip %s", fullSource.c_str());
7974                 continue;
7975                 }
7976                 
7977             String cmd = command;
7978             cmd.append(" ");
7979             cmd.append(fullSource);
7980             cmd.append(" -o ");
7981             cmd.append(fullDest);
7982             
7983             int pos = fullDest.find_last_of('/');
7984             if (pos>0)
7985                 {
7986                 String fullDestPath = fullDest.substr(0, pos);
7987                 if (!createDirectory(fullDestPath))
7988                     return false;
7989                 }
7993             String outString, errString;
7994             if (!executeCommand(cmd.c_str(), "", outString, errString))
7995                 {
7996                 error("<msgfmt> problem: %s", errString.c_str());
7997                 return false;
7998                 }
7999             }
8001         return true;
8002         }
8004     virtual bool parse(Element *elem)
8005         {
8006         if (!parent.getAttribute(elem, "command", commandOpt))
8007             return false;
8008         if (!parent.getAttribute(elem, "todir", toDirNameOpt))
8009             return false;
8010         if (!parent.getAttribute(elem, "out", outNameOpt))
8011             return false;
8012         if (!parent.getAttribute(elem, "owndir", owndirOpt))
8013             return false;
8014             
8015         std::vector<Element *> children = elem->getChildren();
8016         for (unsigned int i=0 ; i<children.size() ; i++)
8017             {
8018             Element *child = children[i];
8019             String tagName = child->getName();
8020             if (tagName == "fileset")
8021                 {
8022                 if (!parseFileSet(child, parent, fileSet))
8023                     return false;
8024                 }
8025             }
8026         return true;
8027         }
8029 private:
8031     FileSet fileSet;
8033     String  commandOpt;
8034     String  toDirNameOpt;
8035     String  outNameOpt;
8036     String  owndirOpt;
8038 };
8042 /**
8043  *  Perform a Package-Config query similar to pkg-config
8044  */
8045 class TaskPkgConfig : public Task
8047 public:
8049     typedef enum
8050         {
8051         PKG_CONFIG_QUERY_CFLAGS,
8052         PKG_CONFIG_QUERY_LIBS,
8053         PKG_CONFIG_QUERY_ALL
8054         } QueryTypes;
8056     TaskPkgConfig(MakeBase &par) : Task(par)
8057         {
8058         type = TASK_PKG_CONFIG;
8059         name = "pkg-config";
8060         }
8062     virtual ~TaskPkgConfig()
8063         {}
8065     virtual bool execute()
8066         {
8067         String pkgName       = parent.eval(pkgNameOpt,      "");
8068         String prefix        = parent.eval(prefixOpt,       "");
8069         String propName      = parent.eval(propNameOpt,     "");
8070         String pkgConfigPath = parent.eval(pkgConfigPathOpt,"");
8071         String query         = parent.eval(queryOpt,        "all");
8073         String path = parent.resolve(pkgConfigPath);
8074         PkgConfig pkgconfig;
8075         pkgconfig.setPath(path);
8076         pkgconfig.setPrefix(prefix);
8077         if (!pkgconfig.query(pkgName))
8078             {
8079             error("<pkg-config> query failed for '%s", name.c_str());
8080             return false;
8081             }
8082             
8083         String val = "";
8084         if (query == "cflags")
8085             val = pkgconfig.getCflags();
8086         else if (query == "libs")
8087             val =pkgconfig.getLibs();
8088         else if (query == "all")
8089             val = pkgconfig.getAll();
8090         else
8091             {
8092             error("<pkg-config> unhandled query : %s", query.c_str());
8093             return false;
8094             }
8095         taskstatus("property %s = '%s'", propName.c_str(), val.c_str());
8096         parent.setProperty(propName, val);
8097         return true;
8098         }
8100     virtual bool parse(Element *elem)
8101         {
8102         //# NAME
8103         if (!parent.getAttribute(elem, "name", pkgNameOpt))
8104             return false;
8105         if (pkgNameOpt.size()==0)
8106             {
8107             error("<pkg-config> requires 'name=\"package\"' attribute");
8108             return false;
8109             }
8111         //# PROPERTY
8112         if (!parent.getAttribute(elem, "property", propNameOpt))
8113             return false;
8114         if (propNameOpt.size()==0)
8115             {
8116             error("<pkg-config> requires 'property=\"name\"' attribute");
8117             return false;
8118             }
8119         //# PATH
8120         if (!parent.getAttribute(elem, "path", pkgConfigPathOpt))
8121             return false;
8122         //# PREFIX
8123         if (!parent.getAttribute(elem, "prefix", prefixOpt))
8124             return false;
8125         //# QUERY
8126         if (!parent.getAttribute(elem, "query", queryOpt))
8127             return false;
8129         return true;
8130         }
8132 private:
8134     String queryOpt;
8135     String pkgNameOpt;
8136     String prefixOpt;
8137     String propNameOpt;
8138     String pkgConfigPathOpt;
8140 };
8147 /**
8148  *  Process an archive to allow random access
8149  */
8150 class TaskRanlib : public Task
8152 public:
8154     TaskRanlib(MakeBase &par) : Task(par)
8155         { type = TASK_RANLIB; name = "ranlib"; }
8157     virtual ~TaskRanlib()
8158         {}
8160     virtual bool execute()
8161         {
8162         String fileName = parent.eval(fileNameOpt, "");
8163         String command  = parent.eval(commandOpt, "ranlib");
8165         String fullName = parent.resolve(fileName);
8166         //trace("fullDir:%s", fullDir.c_str());
8167         String cmd = command;
8168         cmd.append(" ");
8169         cmd.append(fullName);
8170         String outbuf, errbuf;
8171         if (!executeCommand(cmd, "", outbuf, errbuf))
8172             return false;
8173         return true;
8174         }
8176     virtual bool parse(Element *elem)
8177         {
8178         if (!parent.getAttribute(elem, "command", commandOpt))
8179             return false;
8180         if (!parent.getAttribute(elem, "file", fileNameOpt))
8181             return false;
8182         if (fileNameOpt.size() == 0)
8183             {
8184             error("<ranlib> requires 'file=\"fileNname\"' attribute");
8185             return false;
8186             }
8187         return true;
8188         }
8190 private:
8192     String fileNameOpt;
8193     String commandOpt;
8194 };
8198 /**
8199  * Compile a resource file into a binary object
8200  */
8201 class TaskRC : public Task
8203 public:
8205     TaskRC(MakeBase &par) : Task(par)
8206         { type = TASK_RC; name = "rc"; }
8208     virtual ~TaskRC()
8209         {}
8211     virtual bool execute()
8212         {
8213         String command  = parent.eval(commandOpt,  "windres");
8214         String flags    = parent.eval(flagsOpt,    "");
8215         String fileName = parent.eval(fileNameOpt, "");
8216         String outName  = parent.eval(outNameOpt,  "");
8218         String fullFile = parent.resolve(fileName);
8219         String fullOut  = parent.resolve(outName);
8220         if (!isNewerThan(fullFile, fullOut))
8221             return true;
8222         String cmd = command;
8223         cmd.append(" -o ");
8224         cmd.append(fullOut);
8225         cmd.append(" ");
8226         cmd.append(flags);
8227         cmd.append(" ");
8228         cmd.append(fullFile);
8230         String outString, errString;
8231         if (!executeCommand(cmd.c_str(), "", outString, errString))
8232             {
8233             error("RC problem: %s", errString.c_str());
8234             return false;
8235             }
8236         return true;
8237         }
8239     virtual bool parse(Element *elem)
8240         {
8241         if (!parent.getAttribute(elem, "command", commandOpt))
8242             return false;
8243         if (!parent.getAttribute(elem, "file", fileNameOpt))
8244             return false;
8245         if (!parent.getAttribute(elem, "out", outNameOpt))
8246             return false;
8247         std::vector<Element *> children = elem->getChildren();
8248         for (unsigned int i=0 ; i<children.size() ; i++)
8249             {
8250             Element *child = children[i];
8251             String tagName = child->getName();
8252             if (tagName == "flags")
8253                 {
8254                 if (!parent.getValue(child, flagsOpt))
8255                     return false;
8256                 }
8257             }
8258         return true;
8259         }
8261 private:
8263     String commandOpt;
8264     String flagsOpt;
8265     String fileNameOpt;
8266     String outNameOpt;
8268 };
8272 /**
8273  *  Collect .o's into a .so or DLL
8274  */
8275 class TaskSharedLib : public Task
8277 public:
8279     TaskSharedLib(MakeBase &par) : Task(par)
8280         { type = TASK_SHAREDLIB; name = "dll"; }
8282     virtual ~TaskSharedLib()
8283         {}
8285     virtual bool execute()
8286         {
8287         String command     = parent.eval(commandOpt, "dllwrap");
8288         String fileName    = parent.eval(fileNameOpt, "");
8289         String defFileName = parent.eval(defFileNameOpt, "");
8290         String impFileName = parent.eval(impFileNameOpt, "");
8291         String libs        = parent.eval(libsOpt, "");
8293         //trace("###########HERE %d", fileSet.size());
8294         bool doit = false;
8295         
8296         String fullOut = parent.resolve(fileName);
8297         //trace("ar fullout: %s", fullOut.c_str());
8298         
8299         if (!listFiles(parent, fileSet))
8300             return false;
8301         String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
8303         for (unsigned int i=0 ; i<fileSet.size() ; i++)
8304             {
8305             String fname;
8306             if (fileSetDir.size()>0)
8307                 {
8308                 fname.append(fileSetDir);
8309                 fname.append("/");
8310                 }
8311             fname.append(fileSet[i]);
8312             String fullName = parent.resolve(fname);
8313             //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
8314             if (isNewerThan(fullName, fullOut))
8315                 doit = true;
8316             }
8317         //trace("Needs it:%d", doit);
8318         if (!doit)
8319             {
8320             return true;
8321             }
8323         String cmd = "dllwrap";
8324         cmd.append(" -o ");
8325         cmd.append(fullOut);
8326         if (defFileName.size()>0)
8327             {
8328             cmd.append(" --def ");
8329             cmd.append(defFileName);
8330             cmd.append(" ");
8331             }
8332         if (impFileName.size()>0)
8333             {
8334             cmd.append(" --implib ");
8335             cmd.append(impFileName);
8336             cmd.append(" ");
8337             }
8338         for (unsigned int i=0 ; i<fileSet.size() ; i++)
8339             {
8340             String fname;
8341             if (fileSetDir.size()>0)
8342                 {
8343                 fname.append(fileSetDir);
8344                 fname.append("/");
8345                 }
8346             fname.append(fileSet[i]);
8347             String fullName = parent.resolve(fname);
8349             cmd.append(" ");
8350             cmd.append(fullName);
8351             }
8352         cmd.append(" ");
8353         cmd.append(libs);
8355         String outString, errString;
8356         if (!executeCommand(cmd.c_str(), "", outString, errString))
8357             {
8358             error("<sharedlib> problem: %s", errString.c_str());
8359             return false;
8360             }
8362         return true;
8363         }
8365     virtual bool parse(Element *elem)
8366         {
8367         if (!parent.getAttribute(elem, "command", commandOpt))
8368             return false;
8369         if (!parent.getAttribute(elem, "file", fileNameOpt))
8370             return false;
8371         if (!parent.getAttribute(elem, "import", impFileNameOpt))
8372             return false;
8373         if (!parent.getAttribute(elem, "def", defFileNameOpt))
8374             return false;
8375             
8376         std::vector<Element *> children = elem->getChildren();
8377         for (unsigned int i=0 ; i<children.size() ; i++)
8378             {
8379             Element *child = children[i];
8380             String tagName = child->getName();
8381             if (tagName == "fileset")
8382                 {
8383                 if (!parseFileSet(child, parent, fileSet))
8384                     return false;
8385                 }
8386             else if (tagName == "libs")
8387                 {
8388                 if (!parent.getValue(child, libsOpt))
8389                     return false;
8390                 libsOpt = strip(libsOpt);
8391                 }
8392             }
8393         return true;
8394         }
8396 private:
8398     FileSet fileSet;
8400     String commandOpt;
8401     String fileNameOpt;
8402     String defFileNameOpt;
8403     String impFileNameOpt;
8404     String libsOpt;
8406 };
8410 /**
8411  * Run the "ar" command to archive .o's into a .a
8412  */
8413 class TaskStaticLib : public Task
8415 public:
8417     TaskStaticLib(MakeBase &par) : Task(par)
8418         { type = TASK_STATICLIB; name = "staticlib"; }
8420     virtual ~TaskStaticLib()
8421         {}
8423     virtual bool execute()
8424         {
8425         String command = parent.eval(commandOpt, "ar crv");
8426         String fileName = parent.eval(fileNameOpt, "");
8428         bool doit = false;
8429         
8430         String fullOut = parent.resolve(fileName);
8431         //trace("ar fullout: %s", fullOut.c_str());
8432         
8433         if (!listFiles(parent, fileSet))
8434             return false;
8435         String fileSetDir = parent.eval(fileSet.getDirectory(), ".");
8436         //trace("###########HERE %s", fileSetDir.c_str());
8438         for (unsigned int i=0 ; i<fileSet.size() ; i++)
8439             {
8440             String fname;
8441             if (fileSetDir.size()>0)
8442                 {
8443                 fname.append(fileSetDir);
8444                 fname.append("/");
8445                 }
8446             fname.append(fileSet[i]);
8447             String fullName = parent.resolve(fname);
8448             //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
8449             if (isNewerThan(fullName, fullOut))
8450                 doit = true;
8451             }
8452         //trace("Needs it:%d", doit);
8453         if (!doit)
8454             {
8455             return true;
8456             }
8458         String cmd = command;
8459         cmd.append(" ");
8460         cmd.append(fullOut);
8461         for (unsigned int i=0 ; i<fileSet.size() ; i++)
8462             {
8463             String fname;
8464             if (fileSetDir.size()>0)
8465                 {
8466                 fname.append(fileSetDir);
8467                 fname.append("/");
8468                 }
8469             fname.append(fileSet[i]);
8470             String fullName = parent.resolve(fname);
8472             cmd.append(" ");
8473             cmd.append(fullName);
8474             }
8476         String outString, errString;
8477         if (!executeCommand(cmd.c_str(), "", outString, errString))
8478             {
8479             error("<staticlib> problem: %s", errString.c_str());
8480             return false;
8481             }
8483         return true;
8484         }
8487     virtual bool parse(Element *elem)
8488         {
8489         if (!parent.getAttribute(elem, "command", commandOpt))
8490             return false;
8491         if (!parent.getAttribute(elem, "file", fileNameOpt))
8492             return false;
8493             
8494         std::vector<Element *> children = elem->getChildren();
8495         for (unsigned int i=0 ; i<children.size() ; i++)
8496             {
8497             Element *child = children[i];
8498             String tagName = child->getName();
8499             if (tagName == "fileset")
8500                 {
8501                 if (!parseFileSet(child, parent, fileSet))
8502                     return false;
8503                 }
8504             }
8505         return true;
8506         }
8508 private:
8510     FileSet fileSet;
8512     String commandOpt;
8513     String fileNameOpt;
8515 };
8520 /**
8521  * Strip an executable
8522  */
8523 class TaskStrip : public Task
8525 public:
8527     TaskStrip(MakeBase &par) : Task(par)
8528         { type = TASK_STRIP; name = "strip"; }
8530     virtual ~TaskStrip()
8531         {}
8533     virtual bool execute()
8534         {
8535         String command     = parent.eval(commandOpt, "strip");
8536         String fileName    = parent.eval(fileNameOpt, "");
8537         String symFileName = parent.eval(symFileNameOpt, "");
8539         String fullName = parent.resolve(fileName);
8540         //trace("fullDir:%s", fullDir.c_str());
8541         String cmd;
8542         String outbuf, errbuf;
8544         if (symFileName.size()>0)
8545             {
8546             String symFullName = parent.resolve(symFileName);
8547             cmd = "objcopy --only-keep-debug ";
8548             cmd.append(getNativePath(fullName));
8549             cmd.append(" ");
8550             cmd.append(getNativePath(symFullName));
8551             if (!executeCommand(cmd, "", outbuf, errbuf))
8552                 {
8553                 error("<strip> symbol file failed : %s", errbuf.c_str());
8554                 return false;
8555                 }
8556             }
8557             
8558         cmd = command;
8559         cmd.append(getNativePath(fullName));
8560         if (!executeCommand(cmd, "", outbuf, errbuf))
8561             {
8562             error("<strip> failed : %s", errbuf.c_str());
8563             return false;
8564             }
8565         return true;
8566         }
8568     virtual bool parse(Element *elem)
8569         {
8570         if (!parent.getAttribute(elem, "command", commandOpt))
8571             return false;
8572         if (!parent.getAttribute(elem, "file", fileNameOpt))
8573             return false;
8574         if (!parent.getAttribute(elem, "symfile", symFileNameOpt))
8575             return false;
8576         if (fileNameOpt.size() == 0)
8577             {
8578             error("<strip> requires 'file=\"fileName\"' attribute");
8579             return false;
8580             }
8581         return true;
8582         }
8584 private:
8586     String commandOpt;
8587     String fileNameOpt;
8588     String symFileNameOpt;
8589 };
8592 /**
8593  *
8594  */
8595 class TaskTouch : public Task
8597 public:
8599     TaskTouch(MakeBase &par) : Task(par)
8600         { type = TASK_TOUCH; name = "touch"; }
8602     virtual ~TaskTouch()
8603         {}
8605     virtual bool execute()
8606         {
8607         String fileName = parent.eval(fileNameOpt, "");
8609         String fullName = parent.resolve(fileName);
8610         String nativeFile = getNativePath(fullName);
8611         if (!isRegularFile(fullName) && !isDirectory(fullName))
8612             {            
8613             // S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH
8614             int ret = creat(nativeFile.c_str(), 0666);
8615             if (ret != 0) 
8616                 {
8617                 error("<touch> could not create '%s' : %s",
8618                     nativeFile.c_str(), strerror(ret));
8619                 return false;
8620                 }
8621             return true;
8622             }
8623         int ret = utime(nativeFile.c_str(), (struct utimbuf *)0);
8624         if (ret != 0)
8625             {
8626             error("<touch> could not update the modification time for '%s' : %s",
8627                 nativeFile.c_str(), strerror(ret));
8628             return false;
8629             }
8630         return true;
8631         }
8633     virtual bool parse(Element *elem)
8634         {
8635         //trace("touch parse");
8636         if (!parent.getAttribute(elem, "file", fileNameOpt))
8637             return false;
8638         if (fileNameOpt.size() == 0)
8639             {
8640             error("<touch> requires 'file=\"fileName\"' attribute");
8641             return false;
8642             }
8643         return true;
8644         }
8646     String fileNameOpt;
8647 };
8650 /**
8651  *
8652  */
8653 class TaskTstamp : public Task
8655 public:
8657     TaskTstamp(MakeBase &par) : Task(par)
8658         { type = TASK_TSTAMP; name = "tstamp"; }
8660     virtual ~TaskTstamp()
8661         {}
8663     virtual bool execute()
8664         {
8665         return true;
8666         }
8668     virtual bool parse(Element *elem)
8669         {
8670         //trace("tstamp parse");
8671         return true;
8672         }
8673 };
8677 /**
8678  *
8679  */
8680 Task *Task::createTask(Element *elem, int lineNr)
8682     String tagName = elem->getName();
8683     //trace("task:%s", tagName.c_str());
8684     Task *task = NULL;
8685     if (tagName == "cc")
8686         task = new TaskCC(parent);
8687     else if (tagName == "copy")
8688         task = new TaskCopy(parent);
8689     else if (tagName == "cxxtestpart")
8690         task = new TaskCxxTestPart(parent);
8691     else if (tagName == "cxxtestroot")
8692         task = new TaskCxxTestRoot(parent);
8693     else if (tagName == "delete")
8694         task = new TaskDelete(parent);
8695     else if (tagName == "echo")
8696         task = new TaskEcho(parent);
8697     else if (tagName == "jar")
8698         task = new TaskJar(parent);
8699     else if (tagName == "javac")
8700         task = new TaskJavac(parent);
8701     else if (tagName == "link")
8702         task = new TaskLink(parent);
8703     else if (tagName == "makefile")
8704         task = new TaskMakeFile(parent);
8705     else if (tagName == "mkdir")
8706         task = new TaskMkDir(parent);
8707     else if (tagName == "msgfmt")
8708         task = new TaskMsgFmt(parent);
8709     else if (tagName == "pkg-config")
8710         task = new TaskPkgConfig(parent);
8711     else if (tagName == "ranlib")
8712         task = new TaskRanlib(parent);
8713     else if (tagName == "rc")
8714         task = new TaskRC(parent);
8715     else if (tagName == "sharedlib")
8716         task = new TaskSharedLib(parent);
8717     else if (tagName == "staticlib")
8718         task = new TaskStaticLib(parent);
8719     else if (tagName == "strip")
8720         task = new TaskStrip(parent);
8721     else if (tagName == "touch")
8722         task = new TaskTouch(parent);
8723     else if (tagName == "tstamp")
8724         task = new TaskTstamp(parent);
8725     else
8726         {
8727         error("Unknown task '%s'", tagName.c_str());
8728         return NULL;
8729         }
8731     task->setLine(lineNr);
8733     if (!task->parse(elem))
8734         {
8735         delete task;
8736         return NULL;
8737         }
8738     return task;
8743 //########################################################################
8744 //# T A R G E T
8745 //########################################################################
8747 /**
8748  *
8749  */
8750 class Target : public MakeBase
8753 public:
8755     /**
8756      *
8757      */
8758     Target(Make &par) : parent(par)
8759         { init(); }
8761     /**
8762      *
8763      */
8764     Target(const Target &other) : parent(other.parent)
8765         { init(); assign(other); }
8767     /**
8768      *
8769      */
8770     Target &operator=(const Target &other)
8771         { init(); assign(other); return *this; }
8773     /**
8774      *
8775      */
8776     virtual ~Target()
8777         { cleanup() ; }
8780     /**
8781      *
8782      */
8783     virtual Make &getParent()
8784         { return parent; }
8786     /**
8787      *
8788      */
8789     virtual String getName()
8790         { return name; }
8792     /**
8793      *
8794      */
8795     virtual void setName(const String &val)
8796         { name = val; }
8798     /**
8799      *
8800      */
8801     virtual String getDescription()
8802         { return description; }
8804     /**
8805      *
8806      */
8807     virtual void setDescription(const String &val)
8808         { description = val; }
8810     /**
8811      *
8812      */
8813     virtual void addDependency(const String &val)
8814         { deps.push_back(val); }
8816     /**
8817      *
8818      */
8819     virtual void parseDependencies(const String &val)
8820         { deps = tokenize(val, ", "); }
8822     /**
8823      *
8824      */
8825     virtual std::vector<String> &getDependencies()
8826         { return deps; }
8828     /**
8829      *
8830      */
8831     virtual String getIf()
8832         { return ifVar; }
8834     /**
8835      *
8836      */
8837     virtual void setIf(const String &val)
8838         { ifVar = val; }
8840     /**
8841      *
8842      */
8843     virtual String getUnless()
8844         { return unlessVar; }
8846     /**
8847      *
8848      */
8849     virtual void setUnless(const String &val)
8850         { unlessVar = val; }
8852     /**
8853      *
8854      */
8855     virtual void addTask(Task *val)
8856         { tasks.push_back(val); }
8858     /**
8859      *
8860      */
8861     virtual std::vector<Task *> &getTasks()
8862         { return tasks; }
8864 private:
8866     void init()
8867         {
8868         }
8870     void cleanup()
8871         {
8872         tasks.clear();
8873         }
8875     void assign(const Target &other)
8876         {
8877         //parent      = other.parent;
8878         name        = other.name;
8879         description = other.description;
8880         ifVar       = other.ifVar;
8881         unlessVar   = other.unlessVar;
8882         deps        = other.deps;
8883         tasks       = other.tasks;
8884         }
8886     Make &parent;
8888     String name;
8890     String description;
8892     String ifVar;
8894     String unlessVar;
8896     std::vector<String> deps;
8898     std::vector<Task *> tasks;
8900 };
8909 //########################################################################
8910 //# M A K E
8911 //########################################################################
8914 /**
8915  *
8916  */
8917 class Make : public MakeBase
8920 public:
8922     /**
8923      *
8924      */
8925     Make()
8926         { init(); }
8928     /**
8929      *
8930      */
8931     Make(const Make &other)
8932         { assign(other); }
8934     /**
8935      *
8936      */
8937     Make &operator=(const Make &other)
8938         { assign(other); return *this; }
8940     /**
8941      *
8942      */
8943     virtual ~Make()
8944         { cleanup(); }
8946     /**
8947      *
8948      */
8949     virtual std::map<String, Target> &getTargets()
8950         { return targets; }
8953     /**
8954      *
8955      */
8956     virtual String version()
8957         { return BUILDTOOL_VERSION; }
8959     /**
8960      * Overload a <property>
8961      */
8962     virtual bool specifyProperty(const String &name,
8963                                  const String &value);
8965     /**
8966      *
8967      */
8968     virtual bool run();
8970     /**
8971      *
8972      */
8973     virtual bool run(const String &target);
8977 private:
8979     /**
8980      *
8981      */
8982     void init();
8984     /**
8985      *
8986      */
8987     void cleanup();
8989     /**
8990      *
8991      */
8992     void assign(const Make &other);
8994     /**
8995      *
8996      */
8997     bool executeTask(Task &task);
9000     /**
9001      *
9002      */
9003     bool executeTarget(Target &target,
9004              std::set<String> &targetsCompleted);
9007     /**
9008      *
9009      */
9010     bool execute();
9012     /**
9013      *
9014      */
9015     bool checkTargetDependencies(Target &prop,
9016                     std::vector<String> &depList);
9018     /**
9019      *
9020      */
9021     bool parsePropertyFile(const String &fileName,
9022                            const String &prefix);
9024     /**
9025      *
9026      */
9027     bool parseProperty(Element *elem);
9029     /**
9030      *
9031      */
9032     bool parseFile();
9034     /**
9035      *
9036      */
9037     std::vector<String> glob(const String &pattern);
9040     //###############
9041     //# Fields
9042     //###############
9044     String projectName;
9046     String currentTarget;
9048     String defaultTarget;
9050     String specifiedTarget;
9052     String baseDir;
9054     String description;
9055     
9056     //std::vector<Property> properties;
9057     
9058     std::map<String, Target> targets;
9060     std::vector<Task *> allTasks;
9061     
9062     std::map<String, String> specifiedProperties;
9064 };
9067 //########################################################################
9068 //# C L A S S  M A I N T E N A N C E
9069 //########################################################################
9071 /**
9072  *
9073  */
9074 void Make::init()
9076     uri             = "build.xml";
9077     projectName     = "";
9078     currentTarget   = "";
9079     defaultTarget   = "";
9080     specifiedTarget = "";
9081     baseDir         = "";
9082     description     = "";
9083     envPrefix       = "env.";
9084     pcPrefix        = "pc.";
9085     pccPrefix       = "pcc.";
9086     pclPrefix       = "pcl.";
9087     properties.clear();
9088     for (unsigned int i = 0 ; i < allTasks.size() ; i++)
9089         delete allTasks[i];
9090     allTasks.clear();
9095 /**
9096  *
9097  */
9098 void Make::cleanup()
9100     for (unsigned int i = 0 ; i < allTasks.size() ; i++)
9101         delete allTasks[i];
9102     allTasks.clear();
9107 /**
9108  *
9109  */
9110 void Make::assign(const Make &other)
9112     uri              = other.uri;
9113     projectName      = other.projectName;
9114     currentTarget    = other.currentTarget;
9115     defaultTarget    = other.defaultTarget;
9116     specifiedTarget  = other.specifiedTarget;
9117     baseDir          = other.baseDir;
9118     description      = other.description;
9119     properties       = other.properties;
9124 //########################################################################
9125 //# U T I L I T Y    T A S K S
9126 //########################################################################
9128 /**
9129  *  Perform a file globbing
9130  */
9131 std::vector<String> Make::glob(const String &pattern)
9133     std::vector<String> res;
9134     return res;
9138 //########################################################################
9139 //# P U B L I C    A P I
9140 //########################################################################
9144 /**
9145  *
9146  */
9147 bool Make::executeTarget(Target &target,
9148              std::set<String> &targetsCompleted)
9151     String name = target.getName();
9153     //First get any dependencies for this target
9154     std::vector<String> deps = target.getDependencies();
9155     for (unsigned int i=0 ; i<deps.size() ; i++)
9156         {
9157         String dep = deps[i];
9158         //Did we do it already?  Skip
9159         if (targetsCompleted.find(dep)!=targetsCompleted.end())
9160             continue;
9161             
9162         std::map<String, Target> &tgts =
9163                target.getParent().getTargets();
9164         std::map<String, Target>::iterator iter =
9165                tgts.find(dep);
9166         if (iter == tgts.end())
9167             {
9168             error("Target '%s' dependency '%s' not found",
9169                       name.c_str(),  dep.c_str());
9170             return false;
9171             }
9172         Target depTarget = iter->second;
9173         if (!executeTarget(depTarget, targetsCompleted))
9174             {
9175             return false;
9176             }
9177         }
9179     status("##### Target : %s\n##### %s", name.c_str(),
9180             target.getDescription().c_str());
9182     //Now let's do the tasks
9183     std::vector<Task *> &tasks = target.getTasks();
9184     for (unsigned int i=0 ; i<tasks.size() ; i++)
9185         {
9186         Task *task = tasks[i];
9187         status("--- %s / %s", name.c_str(), task->getName().c_str());
9188         if (!task->execute())
9189             {
9190             return false;
9191             }
9192         }
9193         
9194     targetsCompleted.insert(name);
9195     
9196     return true;
9201 /**
9202  *  Main execute() method.  Start here and work
9203  *  up the dependency tree 
9204  */
9205 bool Make::execute()
9207     status("######## EXECUTE");
9209     //Determine initial target
9210     if (specifiedTarget.size()>0)
9211         {
9212         currentTarget = specifiedTarget;
9213         }
9214     else if (defaultTarget.size()>0)
9215         {
9216         currentTarget = defaultTarget;
9217         }
9218     else
9219         {
9220         error("execute: no specified or default target requested");
9221         return false;
9222         }
9224     std::map<String, Target>::iterator iter =
9225                targets.find(currentTarget);
9226     if (iter == targets.end())
9227         {
9228         error("Initial target '%s' not found",
9229                  currentTarget.c_str());
9230         return false;
9231         }
9232         
9233     //Now run
9234     Target target = iter->second;
9235     std::set<String> targetsCompleted;
9236     if (!executeTarget(target, targetsCompleted))
9237         {
9238         return false;
9239         }
9241     status("######## EXECUTE COMPLETE");
9242     return true;
9248 /**
9249  *
9250  */
9251 bool Make::checkTargetDependencies(Target &target, 
9252                             std::vector<String> &depList)
9254     String tgtName = target.getName().c_str();
9255     depList.push_back(tgtName);
9257     std::vector<String> deps = target.getDependencies();
9258     for (unsigned int i=0 ; i<deps.size() ; i++)
9259         {
9260         String dep = deps[i];
9261         //First thing entered was the starting Target
9262         if (dep == depList[0])
9263             {
9264             error("Circular dependency '%s' found at '%s'",
9265                       dep.c_str(), tgtName.c_str());
9266             std::vector<String>::iterator diter;
9267             for (diter=depList.begin() ; diter!=depList.end() ; diter++)
9268                 {
9269                 error("  %s", diter->c_str());
9270                 }
9271             return false;
9272             }
9274         std::map<String, Target> &tgts =
9275                   target.getParent().getTargets();
9276         std::map<String, Target>::iterator titer = tgts.find(dep);
9277         if (titer == tgts.end())
9278             {
9279             error("Target '%s' dependency '%s' not found",
9280                       tgtName.c_str(), dep.c_str());
9281             return false;
9282             }
9283         if (!checkTargetDependencies(titer->second, depList))
9284             {
9285             return false;
9286             }
9287         }
9288     return true;
9295 static int getword(int pos, const String &inbuf, String &result)
9297     int p = pos;
9298     int len = (int)inbuf.size();
9299     String val;
9300     while (p < len)
9301         {
9302         char ch = inbuf[p];
9303         if (!isalnum(ch) && ch!='.' && ch!='_')
9304             break;
9305         val.push_back(ch);
9306         p++;
9307         }
9308     result = val;
9309     return p;
9315 /**
9316  *
9317  */
9318 bool Make::parsePropertyFile(const String &fileName,
9319                              const String &prefix)
9321     FILE *f = fopen(fileName.c_str(), "r");
9322     if (!f)
9323         {
9324         error("could not open property file %s", fileName.c_str());
9325         return false;
9326         }
9327     int linenr = 0;
9328     while (!feof(f))
9329         {
9330         char buf[256];
9331         if (!fgets(buf, 255, f))
9332             break;
9333         linenr++;
9334         String s = buf;
9335         s = trim(s);
9336         int len = s.size();
9337         if (len == 0)
9338             continue;
9339         if (s[0] == '#')
9340             continue;
9341         String key;
9342         String val;
9343         int p = 0;
9344         int p2 = getword(p, s, key);
9345         if (p2 <= p)
9346             {
9347             error("property file %s, line %d: expected keyword",
9348                     fileName.c_str(), linenr);
9349             return false;
9350             }
9351         if (prefix.size() > 0)
9352             {
9353             key.insert(0, prefix);
9354             }
9356         //skip whitespace
9357         for (p=p2 ; p<len ; p++)
9358             if (!isspace(s[p]))
9359                 break;
9361         if (p>=len || s[p]!='=')
9362             {
9363             error("property file %s, line %d: expected '='",
9364                     fileName.c_str(), linenr);
9365             return false;
9366             }
9367         p++;
9369         //skip whitespace
9370         for ( ; p<len ; p++)
9371             if (!isspace(s[p]))
9372                 break;
9374         /* This way expects a word after the =
9375         p2 = getword(p, s, val);
9376         if (p2 <= p)
9377             {
9378             error("property file %s, line %d: expected value",
9379                     fileName.c_str(), linenr);
9380             return false;
9381             }
9382         */
9383         // This way gets the rest of the line after the =
9384         if (p>=len)
9385             {
9386             error("property file %s, line %d: expected value",
9387                     fileName.c_str(), linenr);
9388             return false;
9389             }
9390         val = s.substr(p);
9391         if (key.size()==0)
9392             continue;
9393         //allow property to be set, even if val=""
9395         //trace("key:'%s' val:'%s'", key.c_str(), val.c_str());
9396         //See if we wanted to overload this property
9397         std::map<String, String>::iterator iter =
9398             specifiedProperties.find(key);
9399         if (iter!=specifiedProperties.end())
9400             {
9401             val = iter->second;
9402             status("overloading property '%s' = '%s'",
9403                    key.c_str(), val.c_str());
9404             }
9405         properties[key] = val;
9406         }
9407     fclose(f);
9408     return true;
9414 /**
9415  *
9416  */
9417 bool Make::parseProperty(Element *elem)
9419     std::vector<Attribute> &attrs = elem->getAttributes();
9420     for (unsigned int i=0 ; i<attrs.size() ; i++)
9421         {
9422         String attrName = attrs[i].getName();
9423         String attrVal  = attrs[i].getValue();
9425         if (attrName == "name")
9426             {
9427             String val;
9428             if (!getAttribute(elem, "value", val))
9429                 return false;
9430             if (val.size() > 0)
9431                 {
9432                 properties[attrVal] = val;
9433                 }
9434             else
9435                 {
9436                 if (!getAttribute(elem, "location", val))
9437                     return false;
9438                 //let the property exist, even if not defined
9439                 properties[attrVal] = val;
9440                 }
9441             //See if we wanted to overload this property
9442             std::map<String, String>::iterator iter =
9443                 specifiedProperties.find(attrVal);
9444             if (iter != specifiedProperties.end())
9445                 {
9446                 val = iter->second;
9447                 status("overloading property '%s' = '%s'",
9448                     attrVal.c_str(), val.c_str());
9449                 properties[attrVal] = val;
9450                 }
9451             }
9452         else if (attrName == "file")
9453             {
9454             String prefix;
9455             if (!getAttribute(elem, "prefix", prefix))
9456                 return false;
9457             if (prefix.size() > 0)
9458                 {
9459                 if (prefix[prefix.size()-1] != '.')
9460                     prefix.push_back('.');
9461                 }
9462             if (!parsePropertyFile(attrName, prefix))
9463                 return false;
9464             }
9465         else if (attrName == "environment")
9466             {
9467             if (attrVal.find('.') != attrVal.npos)
9468                 {
9469                 error("environment prefix cannot have a '.' in it");
9470                 return false;
9471                 }
9472             envPrefix = attrVal;
9473             envPrefix.push_back('.');
9474             }
9475         else if (attrName == "pkg-config")
9476             {
9477             if (attrVal.find('.') != attrVal.npos)
9478                 {
9479                 error("pkg-config prefix cannot have a '.' in it");
9480                 return false;
9481                 }
9482             pcPrefix = attrVal;
9483             pcPrefix.push_back('.');
9484             }
9485         else if (attrName == "pkg-config-cflags")
9486             {
9487             if (attrVal.find('.') != attrVal.npos)
9488                 {
9489                 error("pkg-config-cflags prefix cannot have a '.' in it");
9490                 return false;
9491                 }
9492             pccPrefix = attrVal;
9493             pccPrefix.push_back('.');
9494             }
9495         else if (attrName == "pkg-config-libs")
9496             {
9497             if (attrVal.find('.') != attrVal.npos)
9498                 {
9499                 error("pkg-config-libs prefix cannot have a '.' in it");
9500                 return false;
9501                 }
9502             pclPrefix = attrVal;
9503             pclPrefix.push_back('.');
9504             }
9505         }
9507     return true;
9513 /**
9514  *
9515  */
9516 bool Make::parseFile()
9518     status("######## PARSE : %s", uri.getPath().c_str());
9520     setLine(0);
9522     Parser parser;
9523     Element *root = parser.parseFile(uri.getNativePath());
9524     if (!root)
9525         {
9526         error("Could not open %s for reading",
9527               uri.getNativePath().c_str());
9528         return false;
9529         }
9530     
9531     setLine(root->getLine());
9533     if (root->getChildren().size()==0 ||
9534         root->getChildren()[0]->getName()!="project")
9535         {
9536         error("Main xml element should be <project>");
9537         delete root;
9538         return false;
9539         }
9541     //########## Project attributes
9542     Element *project = root->getChildren()[0];
9543     String s = project->getAttribute("name");
9544     if (s.size() > 0)
9545         projectName = s;
9546     s = project->getAttribute("default");
9547     if (s.size() > 0)
9548         defaultTarget = s;
9549     s = project->getAttribute("basedir");
9550     if (s.size() > 0)
9551         baseDir = s;
9553     //######### PARSE MEMBERS
9554     std::vector<Element *> children = project->getChildren();
9555     for (unsigned int i=0 ; i<children.size() ; i++)
9556         {
9557         Element *elem = children[i];
9558         setLine(elem->getLine());
9559         String tagName = elem->getName();
9561         //########## DESCRIPTION
9562         if (tagName == "description")
9563             {
9564             description = parser.trim(elem->getValue());
9565             }
9567         //######### PROPERTY
9568         else if (tagName == "property")
9569             {
9570             if (!parseProperty(elem))
9571                 return false;
9572             }
9574         //######### TARGET
9575         else if (tagName == "target")
9576             {
9577             String tname   = elem->getAttribute("name");
9578             String tdesc   = elem->getAttribute("description");
9579             String tdeps   = elem->getAttribute("depends");
9580             String tif     = elem->getAttribute("if");
9581             String tunless = elem->getAttribute("unless");
9582             Target target(*this);
9583             target.setName(tname);
9584             target.setDescription(tdesc);
9585             target.parseDependencies(tdeps);
9586             target.setIf(tif);
9587             target.setUnless(tunless);
9588             std::vector<Element *> telems = elem->getChildren();
9589             for (unsigned int i=0 ; i<telems.size() ; i++)
9590                 {
9591                 Element *telem = telems[i];
9592                 Task breeder(*this);
9593                 Task *task = breeder.createTask(telem, telem->getLine());
9594                 if (!task)
9595                     return false;
9596                 allTasks.push_back(task);
9597                 target.addTask(task);
9598                 }
9600             //Check name
9601             if (tname.size() == 0)
9602                 {
9603                 error("no name for target");
9604                 return false;
9605                 }
9606             //Check for duplicate name
9607             if (targets.find(tname) != targets.end())
9608                 {
9609                 error("target '%s' already defined", tname.c_str());
9610                 return false;
9611                 }
9612             //more work than targets[tname]=target, but avoids default allocator
9613             targets.insert(std::make_pair<String, Target>(tname, target));
9614             }
9615         //######### none of the above
9616         else
9617             {
9618             error("unknown toplevel tag: <%s>", tagName.c_str());
9619             return false;
9620             }
9622         }
9624     std::map<String, Target>::iterator iter;
9625     for (iter = targets.begin() ; iter!= targets.end() ; iter++)
9626         {
9627         Target tgt = iter->second;
9628         std::vector<String> depList;
9629         if (!checkTargetDependencies(tgt, depList))
9630             {
9631             return false;
9632             }
9633         }
9636     delete root;
9637     status("######## PARSE COMPLETE");
9638     return true;
9642 /**
9643  * Overload a <property>
9644  */
9645 bool Make::specifyProperty(const String &name, const String &value)
9647     if (specifiedProperties.find(name) != specifiedProperties.end())
9648         {
9649         error("Property %s already specified", name.c_str());
9650         return false;
9651         }
9652     specifiedProperties[name] = value;
9653     return true;
9658 /**
9659  *
9660  */
9661 bool Make::run()
9663     if (!parseFile())
9664         return false;
9665         
9666     if (!execute())
9667         return false;
9669     return true;
9675 /**
9676  * Get a formatted MM:SS.sss time elapsed string
9677  */ 
9678 static String
9679 timeDiffString(struct timeval &x, struct timeval &y)
9681     long microsX  = x.tv_usec;
9682     long secondsX = x.tv_sec;
9683     long microsY  = y.tv_usec;
9684     long secondsY = y.tv_sec;
9685     if (microsX < microsY)
9686         {
9687         microsX += 1000000;
9688         secondsX -= 1;
9689         }
9691     int seconds = (int)(secondsX - secondsY);
9692     int millis  = (int)((microsX - microsY)/1000);
9694     int minutes = seconds/60;
9695     seconds -= minutes*60;
9696     char buf[80];
9697     snprintf(buf, 79, "%dm %d.%03ds", minutes, seconds, millis);
9698     String ret = buf;
9699     return ret;
9700     
9703 /**
9704  *
9705  */
9706 bool Make::run(const String &target)
9708     status("####################################################");
9709     status("#   %s", version().c_str());
9710     status("####################################################");
9711     struct timeval timeStart, timeEnd;
9712     ::gettimeofday(&timeStart, NULL);
9713     specifiedTarget = target;
9714     if (!run())
9715         return false;
9716     ::gettimeofday(&timeEnd, NULL);
9717     String timeStr = timeDiffString(timeEnd, timeStart);
9718     status("####################################################");
9719     status("#   BuildTool Completed : %s", timeStr.c_str());
9720     status("####################################################");
9721     return true;
9730 }// namespace buildtool
9731 //########################################################################
9732 //# M A I N
9733 //########################################################################
9735 typedef buildtool::String String;
9737 /**
9738  *  Format an error message in printf() style
9739  */
9740 static void error(const char *fmt, ...)
9742     va_list ap;
9743     va_start(ap, fmt);
9744     fprintf(stderr, "BuildTool error: ");
9745     vfprintf(stderr, fmt, ap);
9746     fprintf(stderr, "\n");
9747     va_end(ap);
9751 static bool parseProperty(const String &s, String &name, String &val)
9753     int len = s.size();
9754     int i;
9755     for (i=0 ; i<len ; i++)
9756         {
9757         char ch = s[i];
9758         if (ch == '=')
9759             break;
9760         name.push_back(ch);
9761         }
9762     if (i>=len || s[i]!='=')
9763         {
9764         error("property requires -Dname=value");
9765         return false;
9766         }
9767     i++;
9768     for ( ; i<len ; i++)
9769         {
9770         char ch = s[i];
9771         val.push_back(ch);
9772         }
9773     return true;
9777 /**
9778  * Compare a buffer with a key, for the length of the key
9779  */
9780 static bool sequ(const String &buf, const char *key)
9782     int len = buf.size();
9783     for (int i=0 ; key[i] && i<len ; i++)
9784         {
9785         if (key[i] != buf[i])
9786             return false;
9787         }        
9788     return true;
9791 static void usage(int argc, char **argv)
9793     printf("usage:\n");
9794     printf("   %s [options] [target]\n", argv[0]);
9795     printf("Options:\n");
9796     printf("  -help, -h              print this message\n");
9797     printf("  -version               print the version information and exit\n");
9798     printf("  -file <file>           use given buildfile\n");
9799     printf("  -f <file>                 ''\n");
9800     printf("  -D<property>=<value>   use value for given property\n");
9806 /**
9807  * Parse the command-line args, get our options,
9808  * and run this thing
9809  */   
9810 static bool parseOptions(int argc, char **argv)
9812     if (argc < 1)
9813         {
9814         error("Cannot parse arguments");
9815         return false;
9816         }
9818     buildtool::Make make;
9820     String target;
9822     //char *progName = argv[0];
9823     for (int i=1 ; i<argc ; i++)
9824         {
9825         String arg = argv[i];
9826         if (arg.size()>1 && arg[0]=='-')
9827             {
9828             if (arg == "-h" || arg == "-help")
9829                 {
9830                 usage(argc,argv);
9831                 return true;
9832                 }
9833             else if (arg == "-version")
9834                 {
9835                 printf("%s", make.version().c_str());
9836                 return true;
9837                 }
9838             else if (arg == "-f" || arg == "-file")
9839                 {
9840                 if (i>=argc)
9841                    {
9842                    usage(argc, argv);
9843                    return false;
9844                    }
9845                 i++; //eat option
9846                 make.setURI(argv[i]);
9847                 }
9848             else if (arg.size()>2 && sequ(arg, "-D"))
9849                 {
9850                 String s = arg.substr(2, arg.size());
9851                 String name, value;
9852                 if (!parseProperty(s, name, value))
9853                    {
9854                    usage(argc, argv);
9855                    return false;
9856                    }
9857                 if (!make.specifyProperty(name, value))
9858                     return false;
9859                 }
9860             else
9861                 {
9862                 error("Unknown option:%s", arg.c_str());
9863                 return false;
9864                 }
9865             }
9866         else
9867             {
9868             if (target.size()>0)
9869                 {
9870                 error("only one initial target");
9871                 usage(argc, argv);
9872                 return false;
9873                 }
9874             target = arg;
9875             }
9876         }
9878     //We have the options.  Now execute them
9879     if (!make.run(target))
9880         return false;
9882     return true;
9888 /*
9889 static bool runMake()
9891     buildtool::Make make;
9892     if (!make.run())
9893         return false;
9894     return true;
9898 static bool pkgConfigTest()
9900     buildtool::PkgConfig pkgConfig;
9901     if (!pkgConfig.readFile("gtk+-2.0.pc"))
9902         return false;
9903     return true;
9908 static bool depTest()
9910     buildtool::DepTool deptool;
9911     deptool.setSourceDirectory("/dev/ink/inkscape/src");
9912     if (!deptool.generateDependencies("build.dep"))
9913         return false;
9914     std::vector<buildtool::FileRec> res =
9915            deptool.loadDepFile("build.dep");
9916     if (res.size() == 0)
9917         return false;
9918     return true;
9921 static bool popenTest()
9923     buildtool::Make make;
9924     buildtool::String out, err;
9925     bool ret = make.executeCommand("gcc xx.cpp", "", out, err);
9926     printf("Popen test:%d '%s' '%s'\n", ret, out.c_str(), err.c_str());
9927     return true;
9931 static bool propFileTest()
9933     buildtool::Make make;
9934     make.parsePropertyFile("test.prop", "test.");
9935     return true;
9937 */
9939 int main(int argc, char **argv)
9942     if (!parseOptions(argc, argv))
9943         return 1;
9944     /*
9945     if (!popenTest())
9946         return 1;
9948     if (!depTest())
9949         return 1;
9950     if (!propFileTest())
9951         return 1;
9952     if (runMake())
9953         return 1;
9954     */
9955     return 0;
9959 //########################################################################
9960 //# E N D 
9961 //########################################################################