Code

Allow overriding output name for msgfmt
[inkscape.git] / buildtool.cpp
1 /**
2  * Simple build automation tool.
3  *
4  * Authors:
5  *   Bob Jamison
6  *
7  * Copyright (C) 2006 Bob Jamison
8  *
9  *  This library is free software; you can redistribute it and/or
10  *  modify it under the terms of the GNU Lesser General Public
11  *  License as published by the Free Software Foundation; either
12  *  version 2.1 of the License, or (at your option) any later version.
13  *
14  *  This library is distributed in the hope that it will be useful,
15  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  *  Lesser General Public License for more details.
18  *
19  *  You should have received a copy of the GNU Lesser General Public
20  *  License along with this library; if not, write to the Free Software
21  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
22  */
24 /**
25  * To use this file, compile with:
26  * <pre>
27  * g++ -O3 buildtool.cpp -o btool.exe
28  * (or whatever your compiler might be) 
29  * Then
30  * btool
31  * or 
32  * btool {target}
33  */  
37 #include <stdio.h>
38 #include <unistd.h>
39 #include <stdarg.h>
40 #include <sys/stat.h>
41 #include <time.h>
42 #include <sys/time.h>
43 #include <dirent.h>
45 #include <string>
46 #include <map>
47 #include <set>
48 #include <vector>
50 #ifdef __WIN32__
51 #include <windows.h>
52 #endif
55 #include <errno.h>
58 //########################################################################
59 //# Definition of gettimeofday() for those who don't have it
60 //########################################################################
61 #ifdef __WIN32__
62 #include <sys/timeb.h>
63 struct timezone {
64       int tz_minuteswest; /* minutes west of Greenwich */
65       int tz_dsttime;     /* type of dst correction */
66     };
68 static int gettimeofday (struct timeval *tv, struct timezone *tz)
69 {
70    struct _timeb tb;
72    if (!tv)
73       return (-1);
75     _ftime (&tb);
76     tv->tv_sec  = tb.time;
77     tv->tv_usec = tb.millitm * 1000 + 500;
78     if (tz)
79         {
80         tz->tz_minuteswest = -60 * _timezone;
81         tz->tz_dsttime = _daylight;
82         }
83     return 0;
84 }
85 #endif
93 namespace buildtool
94 {
99 //########################################################################
100 //########################################################################
101 //##  R E G E X P
102 //########################################################################
103 //########################################################################
105 /**
106  * This is the T-Rex regular expression library, which we
107  * gratefully acknowledge.  It's clean code and small size allow
108  * us to embed it in BuildTool without adding a dependency
109  *
110  */    
112 //begin trex.h
114 #ifndef _TREX_H_
115 #define _TREX_H_
116 /***************************************************************
117     T-Rex a tiny regular expression library
119     Copyright (C) 2003-2006 Alberto Demichelis
121     This software is provided 'as-is', without any express 
122     or implied warranty. In no event will the authors be held 
123     liable for any damages arising from the use of this software.
125     Permission is granted to anyone to use this software for 
126     any purpose, including commercial applications, and to alter
127     it and redistribute it freely, subject to the following restrictions:
129         1. The origin of this software must not be misrepresented;
130         you must not claim that you wrote the original software.
131         If you use this software in a product, an acknowledgment
132         in the product documentation would be appreciated but
133         is not required.
135         2. Altered source versions must be plainly marked as such,
136         and must not be misrepresented as being the original software.
138         3. This notice may not be removed or altered from any
139         source distribution.
141 ****************************************************************/
143 #ifdef _UNICODE
144 #define TRexChar unsigned short
145 #define MAX_CHAR 0xFFFF
146 #define _TREXC(c) L##c 
147 #define trex_strlen wcslen
148 #define trex_printf wprintf
149 #else
150 #define TRexChar char
151 #define MAX_CHAR 0xFF
152 #define _TREXC(c) (c) 
153 #define trex_strlen strlen
154 #define trex_printf printf
155 #endif
157 #ifndef TREX_API
158 #define TREX_API extern
159 #endif
161 #define TRex_True 1
162 #define TRex_False 0
164 typedef unsigned int TRexBool;
165 typedef struct TRex TRex;
167 typedef struct {
168     const TRexChar *begin;
169     int len;
170 } TRexMatch;
172 TREX_API TRex *trex_compile(const TRexChar *pattern,const TRexChar **error);
173 TREX_API void trex_free(TRex *exp);
174 TREX_API TRexBool trex_match(TRex* exp,const TRexChar* text);
175 TREX_API TRexBool trex_search(TRex* exp,const TRexChar* text, const TRexChar** out_begin, const TRexChar** out_end);
176 TREX_API TRexBool trex_searchrange(TRex* exp,const TRexChar* text_begin,const TRexChar* text_end,const TRexChar** out_begin, const TRexChar** out_end);
177 TREX_API int trex_getsubexpcount(TRex* exp);
178 TREX_API TRexBool trex_getsubexp(TRex* exp, int n, TRexMatch *subexp);
180 #endif
182 //end trex.h
184 //start trex.c
187 #include <stdio.h>
188 #include <string>
190 /* see copyright notice in trex.h */
191 #include <string.h>
192 #include <stdlib.h>
193 #include <ctype.h>
194 #include <setjmp.h>
195 //#include "trex.h"
197 #ifdef _UINCODE
198 #define scisprint iswprint
199 #define scstrlen wcslen
200 #define scprintf wprintf
201 #define _SC(x) L(x)
202 #else
203 #define scisprint isprint
204 #define scstrlen strlen
205 #define scprintf printf
206 #define _SC(x) (x)
207 #endif
209 #ifdef _DEBUG
210 #include <stdio.h>
212 static const TRexChar *g_nnames[] =
214     _SC("NONE"),_SC("OP_GREEDY"),    _SC("OP_OR"),
215     _SC("OP_EXPR"),_SC("OP_NOCAPEXPR"),_SC("OP_DOT"),    _SC("OP_CLASS"),
216     _SC("OP_CCLASS"),_SC("OP_NCLASS"),_SC("OP_RANGE"),_SC("OP_CHAR"),
217     _SC("OP_EOL"),_SC("OP_BOL"),_SC("OP_WB")
218 };
220 #endif
221 #define OP_GREEDY        (MAX_CHAR+1) // * + ? {n}
222 #define OP_OR            (MAX_CHAR+2)
223 #define OP_EXPR            (MAX_CHAR+3) //parentesis ()
224 #define OP_NOCAPEXPR    (MAX_CHAR+4) //parentesis (?:)
225 #define OP_DOT            (MAX_CHAR+5)
226 #define OP_CLASS        (MAX_CHAR+6)
227 #define OP_CCLASS        (MAX_CHAR+7)
228 #define OP_NCLASS        (MAX_CHAR+8) //negates class the [^
229 #define OP_RANGE        (MAX_CHAR+9)
230 #define OP_CHAR            (MAX_CHAR+10)
231 #define OP_EOL            (MAX_CHAR+11)
232 #define OP_BOL            (MAX_CHAR+12)
233 #define OP_WB            (MAX_CHAR+13)
235 #define TREX_SYMBOL_ANY_CHAR ('.')
236 #define TREX_SYMBOL_GREEDY_ONE_OR_MORE ('+')
237 #define TREX_SYMBOL_GREEDY_ZERO_OR_MORE ('*')
238 #define TREX_SYMBOL_GREEDY_ZERO_OR_ONE ('?')
239 #define TREX_SYMBOL_BRANCH ('|')
240 #define TREX_SYMBOL_END_OF_STRING ('$')
241 #define TREX_SYMBOL_BEGINNING_OF_STRING ('^')
242 #define TREX_SYMBOL_ESCAPE_CHAR ('\\')
245 typedef int TRexNodeType;
247 typedef struct tagTRexNode{
248     TRexNodeType type;
249     int left;
250     int right;
251     int next;
252 }TRexNode;
254 struct TRex{
255     const TRexChar *_eol;
256     const TRexChar *_bol;
257     const TRexChar *_p;
258     int _first;
259     int _op;
260     TRexNode *_nodes;
261     int _nallocated;
262     int _nsize;
263     int _nsubexpr;
264     TRexMatch *_matches;
265     int _currsubexp;
266     void *_jmpbuf;
267     const TRexChar **_error;
268 };
270 static int trex_list(TRex *exp);
272 static int trex_newnode(TRex *exp, TRexNodeType type)
274     TRexNode n;
275     int newid;
276     n.type = type;
277     n.next = n.right = n.left = -1;
278     if(type == OP_EXPR)
279         n.right = exp->_nsubexpr++;
280     if(exp->_nallocated < (exp->_nsize + 1)) {
281         //int oldsize = exp->_nallocated;
282         exp->_nallocated *= 2;
283         exp->_nodes = (TRexNode *)realloc(exp->_nodes, exp->_nallocated * sizeof(TRexNode));
284     }
285     exp->_nodes[exp->_nsize++] = n;
286     newid = exp->_nsize - 1;
287     return (int)newid;
290 static void trex_error(TRex *exp,const TRexChar *error)
292     if(exp->_error) *exp->_error = error;
293     longjmp(*((jmp_buf*)exp->_jmpbuf),-1);
296 static void trex_expect(TRex *exp, int n){
297     if((*exp->_p) != n) 
298         trex_error(exp, _SC("expected paren"));
299     exp->_p++;
302 static TRexChar trex_escapechar(TRex *exp)
304     if(*exp->_p == TREX_SYMBOL_ESCAPE_CHAR){
305         exp->_p++;
306         switch(*exp->_p) {
307         case 'v': exp->_p++; return '\v';
308         case 'n': exp->_p++; return '\n';
309         case 't': exp->_p++; return '\t';
310         case 'r': exp->_p++; return '\r';
311         case 'f': exp->_p++; return '\f';
312         default: return (*exp->_p++);
313         }
314     } else if(!scisprint(*exp->_p)) trex_error(exp,_SC("letter expected"));
315     return (*exp->_p++);
318 static int trex_charclass(TRex *exp,int classid)
320     int n = trex_newnode(exp,OP_CCLASS);
321     exp->_nodes[n].left = classid;
322     return n;
325 static int trex_charnode(TRex *exp,TRexBool isclass)
327     TRexChar t;
328     if(*exp->_p == TREX_SYMBOL_ESCAPE_CHAR) {
329         exp->_p++;
330         switch(*exp->_p) {
331             case 'n': exp->_p++; return trex_newnode(exp,'\n');
332             case 't': exp->_p++; return trex_newnode(exp,'\t');
333             case 'r': exp->_p++; return trex_newnode(exp,'\r');
334             case 'f': exp->_p++; return trex_newnode(exp,'\f');
335             case 'v': exp->_p++; return trex_newnode(exp,'\v');
336             case 'a': case 'A': case 'w': case 'W': case 's': case 'S': 
337             case 'd': case 'D': case 'x': case 'X': case 'c': case 'C': 
338             case 'p': case 'P': case 'l': case 'u': 
339                 {
340                 t = *exp->_p; exp->_p++; 
341                 return trex_charclass(exp,t);
342                 }
343             case 'b': 
344             case 'B':
345                 if(!isclass) {
346                     int node = trex_newnode(exp,OP_WB);
347                     exp->_nodes[node].left = *exp->_p;
348                     exp->_p++; 
349                     return node;
350                 } //else default
351             default: 
352                 t = *exp->_p; exp->_p++; 
353                 return trex_newnode(exp,t);
354         }
355     }
356     else if(!scisprint(*exp->_p)) {
357         
358         trex_error(exp,_SC("letter expected"));
359     }
360     t = *exp->_p; exp->_p++; 
361     return trex_newnode(exp,t);
363 static int trex_class(TRex *exp)
365     int ret = -1;
366     int first = -1,chain;
367     if(*exp->_p == TREX_SYMBOL_BEGINNING_OF_STRING){
368         ret = trex_newnode(exp,OP_NCLASS);
369         exp->_p++;
370     }else ret = trex_newnode(exp,OP_CLASS);
371     
372     if(*exp->_p == ']') trex_error(exp,_SC("empty class"));
373     chain = ret;
374     while(*exp->_p != ']' && exp->_p != exp->_eol) {
375         if(*exp->_p == '-' && first != -1){ 
376             int r,t;
377             if(*exp->_p++ == ']') trex_error(exp,_SC("unfinished range"));
378             r = trex_newnode(exp,OP_RANGE);
379             if(first>*exp->_p) trex_error(exp,_SC("invalid range"));
380             if(exp->_nodes[first].type == OP_CCLASS) trex_error(exp,_SC("cannot use character classes in ranges"));
381             exp->_nodes[r].left = exp->_nodes[first].type;
382             t = trex_escapechar(exp);
383             exp->_nodes[r].right = t;
384             exp->_nodes[chain].next = r;
385             chain = r;
386             first = -1;
387         }
388         else{
389             if(first!=-1){
390                 int c = first;
391                 exp->_nodes[chain].next = c;
392                 chain = c;
393                 first = trex_charnode(exp,TRex_True);
394             }
395             else{
396                 first = trex_charnode(exp,TRex_True);
397             }
398         }
399     }
400     if(first!=-1){
401         int c = first;
402         exp->_nodes[chain].next = c;
403         chain = c;
404         first = -1;
405     }
406     /* hack? */
407     exp->_nodes[ret].left = exp->_nodes[ret].next;
408     exp->_nodes[ret].next = -1;
409     return ret;
412 static int trex_parsenumber(TRex *exp)
414     int ret = *exp->_p-'0';
415     int positions = 10;
416     exp->_p++;
417     while(isdigit(*exp->_p)) {
418         ret = ret*10+(*exp->_p++-'0');
419         if(positions==1000000000) trex_error(exp,_SC("overflow in numeric constant"));
420         positions *= 10;
421     };
422     return ret;
425 static int trex_element(TRex *exp)
427     int ret = -1;
428     switch(*exp->_p)
429     {
430     case '(': {
431         int expr,newn;
432         exp->_p++;
435         if(*exp->_p =='?') {
436             exp->_p++;
437             trex_expect(exp,':');
438             expr = trex_newnode(exp,OP_NOCAPEXPR);
439         }
440         else
441             expr = trex_newnode(exp,OP_EXPR);
442         newn = trex_list(exp);
443         exp->_nodes[expr].left = newn;
444         ret = expr;
445         trex_expect(exp,')');
446               }
447               break;
448     case '[':
449         exp->_p++;
450         ret = trex_class(exp);
451         trex_expect(exp,']');
452         break;
453     case TREX_SYMBOL_END_OF_STRING: exp->_p++; ret = trex_newnode(exp,OP_EOL);break;
454     case TREX_SYMBOL_ANY_CHAR: exp->_p++; ret = trex_newnode(exp,OP_DOT);break;
455     default:
456         ret = trex_charnode(exp,TRex_False);
457         break;
458     }
460     {
461         int op;
462         TRexBool isgreedy = TRex_False;
463         unsigned short p0 = 0, p1 = 0;
464         switch(*exp->_p){
465             case TREX_SYMBOL_GREEDY_ZERO_OR_MORE: p0 = 0; p1 = 0xFFFF; exp->_p++; isgreedy = TRex_True; break;
466             case TREX_SYMBOL_GREEDY_ONE_OR_MORE: p0 = 1; p1 = 0xFFFF; exp->_p++; isgreedy = TRex_True; break;
467             case TREX_SYMBOL_GREEDY_ZERO_OR_ONE: p0 = 0; p1 = 1; exp->_p++; isgreedy = TRex_True; break;
468             case '{':
469                 exp->_p++;
470                 if(!isdigit(*exp->_p)) trex_error(exp,_SC("number expected"));
471                 p0 = (unsigned short)trex_parsenumber(exp);
472                 /*******************************/
473                 switch(*exp->_p) {
474             case '}':
475                 p1 = p0; exp->_p++;
476                 break;
477             case ',':
478                 exp->_p++;
479                 p1 = 0xFFFF;
480                 if(isdigit(*exp->_p)){
481                     p1 = (unsigned short)trex_parsenumber(exp);
482                 }
483                 trex_expect(exp,'}');
484                 break;
485             default:
486                 trex_error(exp,_SC(", or } expected"));
487         }
488         /*******************************/
489         isgreedy = TRex_True; 
490         break;
492         }
493         if(isgreedy) {
494             int nnode = trex_newnode(exp,OP_GREEDY);
495             op = OP_GREEDY;
496             exp->_nodes[nnode].left = ret;
497             exp->_nodes[nnode].right = ((p0)<<16)|p1;
498             ret = nnode;
499         }
500     }
501     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')) {
502         int nnode = trex_element(exp);
503         exp->_nodes[ret].next = nnode;
504     }
506     return ret;
509 static int trex_list(TRex *exp)
511     int ret=-1,e;
512     if(*exp->_p == TREX_SYMBOL_BEGINNING_OF_STRING) {
513         exp->_p++;
514         ret = trex_newnode(exp,OP_BOL);
515     }
516     e = trex_element(exp);
517     if(ret != -1) {
518         exp->_nodes[ret].next = e;
519     }
520     else ret = e;
522     if(*exp->_p == TREX_SYMBOL_BRANCH) {
523         int temp,tright;
524         exp->_p++;
525         temp = trex_newnode(exp,OP_OR);
526         exp->_nodes[temp].left = ret;
527         tright = trex_list(exp);
528         exp->_nodes[temp].right = tright;
529         ret = temp;
530     }
531     return ret;
534 static TRexBool trex_matchcclass(int cclass,TRexChar c)
536     switch(cclass) {
537     case 'a': return isalpha(c)?TRex_True:TRex_False;
538     case 'A': return !isalpha(c)?TRex_True:TRex_False;
539     case 'w': return (isalnum(c) || c == '_')?TRex_True:TRex_False;
540     case 'W': return (!isalnum(c) && c != '_')?TRex_True:TRex_False;
541     case 's': return isspace(c)?TRex_True:TRex_False;
542     case 'S': return !isspace(c)?TRex_True:TRex_False;
543     case 'd': return isdigit(c)?TRex_True:TRex_False;
544     case 'D': return !isdigit(c)?TRex_True:TRex_False;
545     case 'x': return isxdigit(c)?TRex_True:TRex_False;
546     case 'X': return !isxdigit(c)?TRex_True:TRex_False;
547     case 'c': return iscntrl(c)?TRex_True:TRex_False;
548     case 'C': return !iscntrl(c)?TRex_True:TRex_False;
549     case 'p': return ispunct(c)?TRex_True:TRex_False;
550     case 'P': return !ispunct(c)?TRex_True:TRex_False;
551     case 'l': return islower(c)?TRex_True:TRex_False;
552     case 'u': return isupper(c)?TRex_True:TRex_False;
553     }
554     return TRex_False; /*cannot happen*/
557 static TRexBool trex_matchclass(TRex* exp,TRexNode *node,TRexChar c)
559     do {
560         switch(node->type) {
561             case OP_RANGE:
562                 if(c >= node->left && c <= node->right) return TRex_True;
563                 break;
564             case OP_CCLASS:
565                 if(trex_matchcclass(node->left,c)) return TRex_True;
566                 break;
567             default:
568                 if(c == node->type)return TRex_True;
569         }
570     } while((node->next != -1) && (node = &exp->_nodes[node->next]));
571     return TRex_False;
574 static const TRexChar *trex_matchnode(TRex* exp,TRexNode *node,const TRexChar *str,TRexNode *next)
576     
577     TRexNodeType type = node->type;
578     switch(type) {
579     case OP_GREEDY: {
580         //TRexNode *greedystop = (node->next != -1) ? &exp->_nodes[node->next] : NULL;
581         TRexNode *greedystop = NULL;
582         int p0 = (node->right >> 16)&0x0000FFFF, p1 = node->right&0x0000FFFF, nmaches = 0;
583         const TRexChar *s=str, *good = str;
585         if(node->next != -1) {
586             greedystop = &exp->_nodes[node->next];
587         }
588         else {
589             greedystop = next;
590         }
592         while((nmaches == 0xFFFF || nmaches < p1)) {
594             const TRexChar *stop;
595             if(!(s = trex_matchnode(exp,&exp->_nodes[node->left],s,greedystop)))
596                 break;
597             nmaches++;
598             good=s;
599             if(greedystop) {
600                 //checks that 0 matches satisfy the expression(if so skips)
601                 //if not would always stop(for instance if is a '?')
602                 if(greedystop->type != OP_GREEDY ||
603                 (greedystop->type == OP_GREEDY && ((greedystop->right >> 16)&0x0000FFFF) != 0))
604                 {
605                     TRexNode *gnext = NULL;
606                     if(greedystop->next != -1) {
607                         gnext = &exp->_nodes[greedystop->next];
608                     }else if(next && next->next != -1){
609                         gnext = &exp->_nodes[next->next];
610                     }
611                     stop = trex_matchnode(exp,greedystop,s,gnext);
612                     if(stop) {
613                         //if satisfied stop it
614                         if(p0 == p1 && p0 == nmaches) break;
615                         else if(nmaches >= p0 && p1 == 0xFFFF) break;
616                         else if(nmaches >= p0 && nmaches <= p1) break;
617                     }
618                 }
619             }
620             
621             if(s >= exp->_eol)
622                 break;
623         }
624         if(p0 == p1 && p0 == nmaches) return good;
625         else if(nmaches >= p0 && p1 == 0xFFFF) return good;
626         else if(nmaches >= p0 && nmaches <= p1) return good;
627         return NULL;
628     }
629     case OP_OR: {
630             const TRexChar *asd = str;
631             TRexNode *temp=&exp->_nodes[node->left];
632             while( (asd = trex_matchnode(exp,temp,asd,NULL)) ) {
633                 if(temp->next != -1)
634                     temp = &exp->_nodes[temp->next];
635                 else
636                     return asd;
637             }
638             asd = str;
639             temp = &exp->_nodes[node->right];
640             while( (asd = trex_matchnode(exp,temp,asd,NULL)) ) {
641                 if(temp->next != -1)
642                     temp = &exp->_nodes[temp->next];
643                 else
644                     return asd;
645             }
646             return NULL;
647             break;
648     }
649     case OP_EXPR:
650     case OP_NOCAPEXPR:{
651             TRexNode *n = &exp->_nodes[node->left];
652             const TRexChar *cur = str;
653             int capture = -1;
654             if(node->type != OP_NOCAPEXPR && node->right == exp->_currsubexp) {
655                 capture = exp->_currsubexp;
656                 exp->_matches[capture].begin = cur;
657                 exp->_currsubexp++;
658             }
659             
660             do {
661                 TRexNode *subnext = NULL;
662                 if(n->next != -1) {
663                     subnext = &exp->_nodes[n->next];
664                 }else {
665                     subnext = next;
666                 }
667                 if(!(cur = trex_matchnode(exp,n,cur,subnext))) {
668                     if(capture != -1){
669                         exp->_matches[capture].begin = 0;
670                         exp->_matches[capture].len = 0;
671                     }
672                     return NULL;
673                 }
674             } while((n->next != -1) && (n = &exp->_nodes[n->next]));
676             if(capture != -1) 
677                 exp->_matches[capture].len = cur - exp->_matches[capture].begin;
678             return cur;
679     }                 
680     case OP_WB:
681         if(str == exp->_bol && !isspace(*str)
682          || (str == exp->_eol && !isspace(*(str-1)))
683          || (!isspace(*str) && isspace(*(str+1)))
684          || (isspace(*str) && !isspace(*(str+1))) ) {
685             return (node->left == 'b')?str:NULL;
686         }
687         return (node->left == 'b')?NULL:str;
688     case OP_BOL:
689         if(str == exp->_bol) return str;
690         return NULL;
691     case OP_EOL:
692         if(str == exp->_eol) return str;
693         return NULL;
694     case OP_DOT:{
695         *str++;
696                 }
697         return str;
698     case OP_NCLASS:
699     case OP_CLASS:
700         if(trex_matchclass(exp,&exp->_nodes[node->left],*str)?(type == OP_CLASS?TRex_True:TRex_False):(type == OP_NCLASS?TRex_True:TRex_False)) {
701             *str++;
702             return str;
703         }
704         return NULL;
705     case OP_CCLASS:
706         if(trex_matchcclass(node->left,*str)) {
707             *str++;
708             return str;
709         }
710         return NULL;
711     default: /* char */
712         if(*str != node->type) return NULL;
713         *str++;
714         return str;
715     }
716     return NULL;
719 /* public api */
720 TRex *trex_compile(const TRexChar *pattern,const TRexChar **error)
722     TRex *exp = (TRex *)malloc(sizeof(TRex));
723     exp->_eol = exp->_bol = NULL;
724     exp->_p = pattern;
725     exp->_nallocated = (int)scstrlen(pattern) * sizeof(TRexChar);
726     exp->_nodes = (TRexNode *)malloc(exp->_nallocated * sizeof(TRexNode));
727     exp->_nsize = 0;
728     exp->_matches = 0;
729     exp->_nsubexpr = 0;
730     exp->_first = trex_newnode(exp,OP_EXPR);
731     exp->_error = error;
732     exp->_jmpbuf = malloc(sizeof(jmp_buf));
733     if(setjmp(*((jmp_buf*)exp->_jmpbuf)) == 0) {
734         int res = trex_list(exp);
735         exp->_nodes[exp->_first].left = res;
736         if(*exp->_p!='\0')
737             trex_error(exp,_SC("unexpected character"));
738 #ifdef _DEBUG
739         {
740             int nsize,i;
741             TRexNode *t;
742             nsize = exp->_nsize;
743             t = &exp->_nodes[0];
744             scprintf(_SC("\n"));
745             for(i = 0;i < nsize; i++) {
746                 if(exp->_nodes[i].type>MAX_CHAR)
747                     scprintf(_SC("[%02d] %10s "),i,g_nnames[exp->_nodes[i].type-MAX_CHAR]);
748                 else
749                     scprintf(_SC("[%02d] %10c "),i,exp->_nodes[i].type);
750                 scprintf(_SC("left %02d right %02d next %02d\n"),exp->_nodes[i].left,exp->_nodes[i].right,exp->_nodes[i].next);
751             }
752             scprintf(_SC("\n"));
753         }
754 #endif
755         exp->_matches = (TRexMatch *) malloc(exp->_nsubexpr * sizeof(TRexMatch));
756         memset(exp->_matches,0,exp->_nsubexpr * sizeof(TRexMatch));
757     }
758     else{
759         trex_free(exp);
760         return NULL;
761     }
762     return exp;
765 void trex_free(TRex *exp)
767     if(exp)    {
768         if(exp->_nodes) free(exp->_nodes);
769         if(exp->_jmpbuf) free(exp->_jmpbuf);
770         if(exp->_matches) free(exp->_matches);
771         free(exp);
772     }
775 TRexBool trex_match(TRex* exp,const TRexChar* text)
777     const TRexChar* res = NULL;
778     exp->_bol = text;
779     exp->_eol = text + scstrlen(text);
780     exp->_currsubexp = 0;
781     res = trex_matchnode(exp,exp->_nodes,text,NULL);
782     if(res == NULL || res != exp->_eol)
783         return TRex_False;
784     return TRex_True;
787 TRexBool trex_searchrange(TRex* exp,const TRexChar* text_begin,const TRexChar* text_end,const TRexChar** out_begin, const TRexChar** out_end)
789     const TRexChar *cur = NULL;
790     int node = exp->_first;
791     if(text_begin >= text_end) return TRex_False;
792     exp->_bol = text_begin;
793     exp->_eol = text_end;
794     do {
795         cur = text_begin;
796         while(node != -1) {
797             exp->_currsubexp = 0;
798             cur = trex_matchnode(exp,&exp->_nodes[node],cur,NULL);
799             if(!cur)
800                 break;
801             node = exp->_nodes[node].next;
802         }
803         *text_begin++;
804     } while(cur == NULL && text_begin != text_end);
806     if(cur == NULL)
807         return TRex_False;
809     --text_begin;
811     if(out_begin) *out_begin = text_begin;
812     if(out_end) *out_end = cur;
813     return TRex_True;
816 TRexBool trex_search(TRex* exp,const TRexChar* text, const TRexChar** out_begin, const TRexChar** out_end)
818     return trex_searchrange(exp,text,text + scstrlen(text),out_begin,out_end);
821 int trex_getsubexpcount(TRex* exp)
823     return exp->_nsubexpr;
826 TRexBool trex_getsubexp(TRex* exp, int n, TRexMatch *subexp)
828     if( n<0 || n >= exp->_nsubexpr) return TRex_False;
829     *subexp = exp->_matches[n];
830     return TRex_True;
834 //########################################################################
835 //########################################################################
836 //##  E N D    R E G E X P
837 //########################################################################
838 //########################################################################
844 //########################################################################
845 //########################################################################
846 //##  X M L
847 //########################################################################
848 //########################################################################
850 // Note:  This mini-dom library comes from Pedro, another little project
851 // of mine.
853 typedef std::string String;
854 typedef unsigned int XMLCh;
857 class Namespace
859 public:
860     Namespace()
861         {}
863     Namespace(const String &prefixArg, const String &namespaceURIArg)
864         {
865         prefix       = prefixArg;
866         namespaceURI = namespaceURIArg;
867         }
869     Namespace(const Namespace &other)
870         {
871         assign(other);
872         }
874     Namespace &operator=(const Namespace &other)
875         {
876         assign(other);
877         return *this;
878         }
880     virtual ~Namespace()
881         {}
883     virtual String getPrefix()
884         { return prefix; }
886     virtual String getNamespaceURI()
887         { return namespaceURI; }
889 protected:
891     void assign(const Namespace &other)
892         {
893         prefix       = other.prefix;
894         namespaceURI = other.namespaceURI;
895         }
897     String prefix;
898     String namespaceURI;
900 };
902 class Attribute
904 public:
905     Attribute()
906         {}
908     Attribute(const String &nameArg, const String &valueArg)
909         {
910         name  = nameArg;
911         value = valueArg;
912         }
914     Attribute(const Attribute &other)
915         {
916         assign(other);
917         }
919     Attribute &operator=(const Attribute &other)
920         {
921         assign(other);
922         return *this;
923         }
925     virtual ~Attribute()
926         {}
928     virtual String getName()
929         { return name; }
931     virtual String getValue()
932         { return value; }
934 protected:
936     void assign(const Attribute &other)
937         {
938         name  = other.name;
939         value = other.value;
940         }
942     String name;
943     String value;
945 };
948 class Element
950 friend class Parser;
952 public:
953     Element()
954         {
955         parent = NULL;
956         }
958     Element(const String &nameArg)
959         {
960         parent = NULL;
961         name   = nameArg;
962         }
964     Element(const String &nameArg, const String &valueArg)
965         {
966         parent = NULL;
967         name   = nameArg;
968         value  = valueArg;
969         }
971     Element(const Element &other)
972         {
973         assign(other);
974         }
976     Element &operator=(const Element &other)
977         {
978         assign(other);
979         return *this;
980         }
982     virtual Element *clone();
984     virtual ~Element()
985         {
986         for (unsigned int i=0 ; i<children.size() ; i++)
987             delete children[i];
988         }
990     virtual String getName()
991         { return name; }
993     virtual String getValue()
994         { return value; }
996     Element *getParent()
997         { return parent; }
999     std::vector<Element *> getChildren()
1000         { return children; }
1002     std::vector<Element *> findElements(const String &name);
1004     String getAttribute(const String &name);
1006     std::vector<Attribute> &getAttributes()
1007         { return attributes; } 
1009     String getTagAttribute(const String &tagName, const String &attrName);
1011     String getTagValue(const String &tagName);
1013     void addChild(Element *child);
1015     void addAttribute(const String &name, const String &value);
1017     void addNamespace(const String &prefix, const String &namespaceURI);
1020     /**
1021      * Prettyprint an XML tree to an output stream.  Elements are indented
1022      * according to element hierarchy.
1023      * @param f a stream to receive the output
1024      * @param elem the element to output
1025      */
1026     void writeIndented(FILE *f);
1028     /**
1029      * Prettyprint an XML tree to standard output.  This is the equivalent of
1030      * writeIndented(stdout).
1031      * @param elem the element to output
1032      */
1033     void print();
1035 protected:
1037     void assign(const Element &other)
1038         {
1039         parent     = other.parent;
1040         children   = other.children;
1041         attributes = other.attributes;
1042         namespaces = other.namespaces;
1043         name       = other.name;
1044         value      = other.value;
1045         }
1047     void findElementsRecursive(std::vector<Element *>&res, const String &name);
1049     void writeIndentedRecursive(FILE *f, int indent);
1051     Element *parent;
1053     std::vector<Element *>children;
1055     std::vector<Attribute> attributes;
1056     std::vector<Namespace> namespaces;
1058     String name;
1059     String value;
1061 };
1067 class Parser
1069 public:
1070     /**
1071      * Constructor
1072      */
1073     Parser()
1074         { init(); }
1076     virtual ~Parser()
1077         {}
1079     /**
1080      * Parse XML in a char buffer.
1081      * @param buf a character buffer to parse
1082      * @param pos position to start parsing
1083      * @param len number of chars, from pos, to parse.
1084      * @return a pointer to the root of the XML document;
1085      */
1086     Element *parse(const char *buf,int pos,int len);
1088     /**
1089      * Parse XML in a char buffer.
1090      * @param buf a character buffer to parse
1091      * @param pos position to start parsing
1092      * @param len number of chars, from pos, to parse.
1093      * @return a pointer to the root of the XML document;
1094      */
1095     Element *parse(const String &buf);
1097     /**
1098      * Parse a named XML file.  The file is loaded like a data file;
1099      * the original format is not preserved.
1100      * @param fileName the name of the file to read
1101      * @return a pointer to the root of the XML document;
1102      */
1103     Element *parseFile(const String &fileName);
1105     /**
1106      * Utility method to preprocess a string for XML
1107      * output, escaping its entities.
1108      * @param str the string to encode
1109      */
1110     static String encode(const String &str);
1112     /**
1113      *  Removes whitespace from beginning and end of a string
1114      */
1115     String trim(const String &s);
1117 private:
1119     void init()
1120         {
1121         keepGoing       = true;
1122         currentNode     = NULL;
1123         parselen        = 0;
1124         parsebuf        = NULL;
1125         currentPosition = 0;
1126         }
1128     void getLineAndColumn(long pos, long *lineNr, long *colNr);
1130     void error(char *fmt, ...);
1132     int peek(long pos);
1134     int match(long pos, const char *text);
1136     int skipwhite(long p);
1138     int getWord(int p0, String &buf);
1140     int getQuoted(int p0, String &buf, int do_i_parse);
1142     int parseVersion(int p0);
1144     int parseDoctype(int p0);
1146     int parseElement(int p0, Element *par,int depth);
1148     Element *parse(XMLCh *buf,int pos,int len);
1150     bool       keepGoing;
1151     Element    *currentNode;
1152     long       parselen;
1153     XMLCh      *parsebuf;
1154     String  cdatabuf;
1155     long       currentPosition;
1156     int        colNr;
1158 };
1163 //########################################################################
1164 //# E L E M E N T
1165 //########################################################################
1167 Element *Element::clone()
1169     Element *elem = new Element(name, value);
1170     elem->parent     = parent;
1171     elem->attributes = attributes;
1172     elem->namespaces = namespaces;
1174     std::vector<Element *>::iterator iter;
1175     for (iter = children.begin(); iter != children.end() ; iter++)
1176         {
1177         elem->addChild((*iter)->clone());
1178         }
1179     return elem;
1183 void Element::findElementsRecursive(std::vector<Element *>&res, const String &name)
1185     if (getName() == name)
1186         {
1187         res.push_back(this);
1188         }
1189     for (unsigned int i=0; i<children.size() ; i++)
1190         children[i]->findElementsRecursive(res, name);
1193 std::vector<Element *> Element::findElements(const String &name)
1195     std::vector<Element *> res;
1196     findElementsRecursive(res, name);
1197     return res;
1200 String Element::getAttribute(const String &name)
1202     for (unsigned int i=0 ; i<attributes.size() ; i++)
1203         if (attributes[i].getName() ==name)
1204             return attributes[i].getValue();
1205     return "";
1208 String Element::getTagAttribute(const String &tagName, const String &attrName)
1210     std::vector<Element *>elems = findElements(tagName);
1211     if (elems.size() <1)
1212         return "";
1213     String res = elems[0]->getAttribute(attrName);
1214     return res;
1217 String Element::getTagValue(const String &tagName)
1219     std::vector<Element *>elems = findElements(tagName);
1220     if (elems.size() <1)
1221         return "";
1222     String res = elems[0]->getValue();
1223     return res;
1226 void Element::addChild(Element *child)
1228     if (!child)
1229         return;
1230     child->parent = this;
1231     children.push_back(child);
1235 void Element::addAttribute(const String &name, const String &value)
1237     Attribute attr(name, value);
1238     attributes.push_back(attr);
1241 void Element::addNamespace(const String &prefix, const String &namespaceURI)
1243     Namespace ns(prefix, namespaceURI);
1244     namespaces.push_back(ns);
1247 void Element::writeIndentedRecursive(FILE *f, int indent)
1249     int i;
1250     if (!f)
1251         return;
1252     //Opening tag, and attributes
1253     for (i=0;i<indent;i++)
1254         fputc(' ',f);
1255     fprintf(f,"<%s",name.c_str());
1256     for (unsigned int i=0 ; i<attributes.size() ; i++)
1257         {
1258         fprintf(f," %s=\"%s\"",
1259               attributes[i].getName().c_str(),
1260               attributes[i].getValue().c_str());
1261         }
1262     for (unsigned int i=0 ; i<namespaces.size() ; i++)
1263         {
1264         fprintf(f," xmlns:%s=\"%s\"",
1265               namespaces[i].getPrefix().c_str(),
1266               namespaces[i].getNamespaceURI().c_str());
1267         }
1268     fprintf(f,">\n");
1270     //Between the tags
1271     if (value.size() > 0)
1272         {
1273         for (int i=0;i<indent;i++)
1274             fputc(' ', f);
1275         fprintf(f," %s\n", value.c_str());
1276         }
1278     for (unsigned int i=0 ; i<children.size() ; i++)
1279         children[i]->writeIndentedRecursive(f, indent+2);
1281     //Closing tag
1282     for (int i=0; i<indent; i++)
1283         fputc(' ',f);
1284     fprintf(f,"</%s>\n", name.c_str());
1287 void Element::writeIndented(FILE *f)
1289     writeIndentedRecursive(f, 0);
1292 void Element::print()
1294     writeIndented(stdout);
1298 //########################################################################
1299 //# P A R S E R
1300 //########################################################################
1304 typedef struct
1305     {
1306     char *escaped;
1307     char value;
1308     } EntityEntry;
1310 static EntityEntry entities[] =
1312     { "&amp;" , '&'  },
1313     { "&lt;"  , '<'  },
1314     { "&gt;"  , '>'  },
1315     { "&apos;", '\'' },
1316     { "&quot;", '"'  },
1317     { NULL    , '\0' }
1318 };
1322 /**
1323  *  Removes whitespace from beginning and end of a string
1324  */
1325 String Parser::trim(const String &s)
1327     if (s.size() < 1)
1328         return s;
1329     
1330     //Find first non-ws char
1331     unsigned int begin = 0;
1332     for ( ; begin < s.size() ; begin++)
1333         {
1334         if (!isspace(s[begin]))
1335             break;
1336         }
1338     //Find first non-ws char, going in reverse
1339     unsigned int end = s.size() - 1;
1340     for ( ; end > begin ; end--)
1341         {
1342         if (!isspace(s[end]))
1343             break;
1344         }
1345     //trace("begin:%d  end:%d", begin, end);
1347     String res = s.substr(begin, end-begin+1);
1348     return res;
1351 void Parser::getLineAndColumn(long pos, long *lineNr, long *colNr)
1353     long line = 1;
1354     long col  = 1;
1355     for (long i=0 ; i<pos ; i++)
1356         {
1357         XMLCh ch = parsebuf[i];
1358         if (ch == '\n' || ch == '\r')
1359             {
1360             col = 0;
1361             line ++;
1362             }
1363         else
1364             col++;
1365         }
1366     *lineNr = line;
1367     *colNr  = col;
1372 void Parser::error(char *fmt, ...)
1374     long lineNr;
1375     long colNr;
1376     getLineAndColumn(currentPosition, &lineNr, &colNr);
1377     va_list args;
1378     fprintf(stderr, "xml error at line %ld, column %ld:", lineNr, colNr);
1379     va_start(args,fmt);
1380     vfprintf(stderr,fmt,args);
1381     va_end(args) ;
1382     fprintf(stderr, "\n");
1387 int Parser::peek(long pos)
1389     if (pos >= parselen)
1390         return -1;
1391     currentPosition = pos;
1392     int ch = parsebuf[pos];
1393     //printf("ch:%c\n", ch);
1394     return ch;
1399 String Parser::encode(const String &str)
1401     String ret;
1402     for (unsigned int i=0 ; i<str.size() ; i++)
1403         {
1404         XMLCh ch = (XMLCh)str[i];
1405         if (ch == '&')
1406             ret.append("&amp;");
1407         else if (ch == '<')
1408             ret.append("&lt;");
1409         else if (ch == '>')
1410             ret.append("&gt;");
1411         else if (ch == '\'')
1412             ret.append("&apos;");
1413         else if (ch == '"')
1414             ret.append("&quot;");
1415         else
1416             ret.push_back(ch);
1418         }
1419     return ret;
1423 int Parser::match(long p0, const char *text)
1425     int p = p0;
1426     while (*text)
1427         {
1428         if (peek(p) != *text)
1429             return p0;
1430         p++; text++;
1431         }
1432     return p;
1437 int Parser::skipwhite(long p)
1440     while (p<parselen)
1441         {
1442         int p2 = match(p, "<!--");
1443         if (p2 > p)
1444             {
1445             p = p2;
1446             while (p<parselen)
1447               {
1448               p2 = match(p, "-->");
1449               if (p2 > p)
1450                   {
1451                   p = p2;
1452                   break;
1453                   }
1454               p++;
1455               }
1456           }
1457       XMLCh b = peek(p);
1458       if (!isspace(b))
1459           break;
1460       p++;
1461       }
1462   return p;
1465 /* modify this to allow all chars for an element or attribute name*/
1466 int Parser::getWord(int p0, String &buf)
1468     int p = p0;
1469     while (p<parselen)
1470         {
1471         XMLCh b = peek(p);
1472         if (b<=' ' || b=='/' || b=='>' || b=='=')
1473             break;
1474         buf.push_back(b);
1475         p++;
1476         }
1477     return p;
1480 int Parser::getQuoted(int p0, String &buf, int do_i_parse)
1483     int p = p0;
1484     if (peek(p) != '"' && peek(p) != '\'')
1485         return p0;
1486     p++;
1488     while ( p<parselen )
1489         {
1490         XMLCh b = peek(p);
1491         if (b=='"' || b=='\'')
1492             break;
1493         if (b=='&' && do_i_parse)
1494             {
1495             bool found = false;
1496             for (EntityEntry *ee = entities ; ee->value ; ee++)
1497                 {
1498                 int p2 = match(p, ee->escaped);
1499                 if (p2>p)
1500                     {
1501                     buf.push_back(ee->value);
1502                     p = p2;
1503                     found = true;
1504                     break;
1505                     }
1506                 }
1507             if (!found)
1508                 {
1509                 error("unterminated entity");
1510                 return false;
1511                 }
1512             }
1513         else
1514             {
1515             buf.push_back(b);
1516             p++;
1517             }
1518         }
1519     return p;
1522 int Parser::parseVersion(int p0)
1524     //printf("### parseVersion: %d\n", p0);
1526     int p = p0;
1528     p = skipwhite(p0);
1530     if (peek(p) != '<')
1531         return p0;
1533     p++;
1534     if (p>=parselen || peek(p)!='?')
1535         return p0;
1537     p++;
1539     String buf;
1541     while (p<parselen)
1542         {
1543         XMLCh ch = peek(p);
1544         if (ch=='?')
1545             {
1546             p++;
1547             break;
1548             }
1549         buf.push_back(ch);
1550         p++;
1551         }
1553     if (peek(p) != '>')
1554         return p0;
1555     p++;
1557     //printf("Got version:%s\n",buf.c_str());
1558     return p;
1561 int Parser::parseDoctype(int p0)
1563     //printf("### parseDoctype: %d\n", p0);
1565     int p = p0;
1566     p = skipwhite(p);
1568     if (p>=parselen || peek(p)!='<')
1569         return p0;
1571     p++;
1573     if (peek(p)!='!' || peek(p+1)=='-')
1574         return p0;
1575     p++;
1577     String buf;
1578     while (p<parselen)
1579         {
1580         XMLCh ch = peek(p);
1581         if (ch=='>')
1582             {
1583             p++;
1584             break;
1585             }
1586         buf.push_back(ch);
1587         p++;
1588         }
1590     //printf("Got doctype:%s\n",buf.c_str());
1591     return p;
1594 int Parser::parseElement(int p0, Element *par,int depth)
1597     int p = p0;
1599     int p2 = p;
1601     p = skipwhite(p);
1603     //## Get open tag
1604     XMLCh ch = peek(p);
1605     if (ch!='<')
1606         return p0;
1608     p++;
1610     String openTagName;
1611     p = skipwhite(p);
1612     p = getWord(p, openTagName);
1613     //printf("####tag :%s\n", openTagName.c_str());
1614     p = skipwhite(p);
1616     //Add element to tree
1617     Element *n = new Element(openTagName);
1618     n->parent = par;
1619     par->addChild(n);
1621     // Get attributes
1622     if (peek(p) != '>')
1623         {
1624         while (p<parselen)
1625             {
1626             p = skipwhite(p);
1627             ch = peek(p);
1628             //printf("ch:%c\n",ch);
1629             if (ch=='>')
1630                 break;
1631             else if (ch=='/' && p<parselen+1)
1632                 {
1633                 p++;
1634                 p = skipwhite(p);
1635                 ch = peek(p);
1636                 if (ch=='>')
1637                     {
1638                     p++;
1639                     //printf("quick close\n");
1640                     return p;
1641                     }
1642                 }
1643             String attrName;
1644             p2 = getWord(p, attrName);
1645             if (p2==p)
1646                 break;
1647             //printf("name:%s",buf);
1648             p=p2;
1649             p = skipwhite(p);
1650             ch = peek(p);
1651             //printf("ch:%c\n",ch);
1652             if (ch!='=')
1653                 break;
1654             p++;
1655             p = skipwhite(p);
1656             // ch = parsebuf[p];
1657             // printf("ch:%c\n",ch);
1658             String attrVal;
1659             p2 = getQuoted(p, attrVal, true);
1660             p=p2+1;
1661             //printf("name:'%s'   value:'%s'\n",attrName.c_str(),attrVal.c_str());
1662             char *namestr = (char *)attrName.c_str();
1663             if (strncmp(namestr, "xmlns:", 6)==0)
1664                 n->addNamespace(attrName, attrVal);
1665             else
1666                 n->addAttribute(attrName, attrVal);
1667             }
1668         }
1670     bool cdata = false;
1672     p++;
1673     // ### Get intervening data ### */
1674     String data;
1675     while (p<parselen)
1676         {
1677         //# COMMENT
1678         p2 = match(p, "<!--");
1679         if (!cdata && p2>p)
1680             {
1681             p = p2;
1682             while (p<parselen)
1683                 {
1684                 p2 = match(p, "-->");
1685                 if (p2 > p)
1686                     {
1687                     p = p2;
1688                     break;
1689                     }
1690                 p++;
1691                 }
1692             }
1694         ch = peek(p);
1695         //# END TAG
1696         if (ch=='<' && !cdata && peek(p+1)=='/')
1697             {
1698             break;
1699             }
1700         //# CDATA
1701         p2 = match(p, "<![CDATA[");
1702         if (p2 > p)
1703             {
1704             cdata = true;
1705             p = p2;
1706             continue;
1707             }
1709         //# CHILD ELEMENT
1710         if (ch == '<')
1711             {
1712             p2 = parseElement(p, n, depth+1);
1713             if (p2 == p)
1714                 {
1715                 /*
1716                 printf("problem on element:%s.  p2:%d p:%d\n",
1717                       openTagName.c_str(), p2, p);
1718                 */
1719                 return p0;
1720                 }
1721             p = p2;
1722             continue;
1723             }
1724         //# ENTITY
1725         if (ch=='&' && !cdata)
1726             {
1727             bool found = false;
1728             for (EntityEntry *ee = entities ; ee->value ; ee++)
1729                 {
1730                 int p2 = match(p, ee->escaped);
1731                 if (p2>p)
1732                     {
1733                     data.push_back(ee->value);
1734                     p = p2;
1735                     found = true;
1736                     break;
1737                     }
1738                 }
1739             if (!found)
1740                 {
1741                 error("unterminated entity");
1742                 return -1;
1743                 }
1744             continue;
1745             }
1747         //# NONE OF THE ABOVE
1748         data.push_back(ch);
1749         p++;
1750         }/*while*/
1753     n->value = data;
1754     //printf("%d : data:%s\n",p,data.c_str());
1756     //## Get close tag
1757     p = skipwhite(p);
1758     ch = peek(p);
1759     if (ch != '<')
1760         {
1761         error("no < for end tag\n");
1762         return p0;
1763         }
1764     p++;
1765     ch = peek(p);
1766     if (ch != '/')
1767         {
1768         error("no / on end tag");
1769         return p0;
1770         }
1771     p++;
1772     ch = peek(p);
1773     p = skipwhite(p);
1774     String closeTagName;
1775     p = getWord(p, closeTagName);
1776     if (openTagName != closeTagName)
1777         {
1778         error("Mismatched closing tag.  Expected </%S>. Got '%S'.",
1779                 openTagName.c_str(), closeTagName.c_str());
1780         return p0;
1781         }
1782     p = skipwhite(p);
1783     if (peek(p) != '>')
1784         {
1785         error("no > on end tag for '%s'", closeTagName.c_str());
1786         return p0;
1787         }
1788     p++;
1789     // printf("close element:%s\n",closeTagName.c_str());
1790     p = skipwhite(p);
1791     return p;
1797 Element *Parser::parse(XMLCh *buf,int pos,int len)
1799     parselen = len;
1800     parsebuf = buf;
1801     Element *rootNode = new Element("root");
1802     pos = parseVersion(pos);
1803     pos = parseDoctype(pos);
1804     pos = parseElement(pos, rootNode, 0);
1805     return rootNode;
1809 Element *Parser::parse(const char *buf, int pos, int len)
1811     XMLCh *charbuf = new XMLCh[len + 1];
1812     long i = 0;
1813     for ( ; i < len ; i++)
1814         charbuf[i] = (XMLCh)buf[i];
1815     charbuf[i] = '\0';
1817     Element *n = parse(charbuf, pos, len);
1818     delete[] charbuf;
1819     return n;
1822 Element *Parser::parse(const String &buf)
1824     long len = (long)buf.size();
1825     XMLCh *charbuf = new XMLCh[len + 1];
1826     long i = 0;
1827     for ( ; i < len ; i++)
1828         charbuf[i] = (XMLCh)buf[i];
1829     charbuf[i] = '\0';
1831     Element *n = parse(charbuf, 0, len);
1832     delete[] charbuf;
1833     return n;
1836 Element *Parser::parseFile(const String &fileName)
1839     //##### LOAD INTO A CHAR BUF, THEN CONVERT TO XMLCh
1840     FILE *f = fopen(fileName.c_str(), "rb");
1841     if (!f)
1842         return NULL;
1844     struct stat  statBuf;
1845     if (fstat(fileno(f),&statBuf)<0)
1846         {
1847         fclose(f);
1848         return NULL;
1849         }
1850     long filelen = statBuf.st_size;
1852     //printf("length:%d\n",filelen);
1853     XMLCh *charbuf = new XMLCh[filelen + 1];
1854     for (XMLCh *p=charbuf ; !feof(f) ; p++)
1855         {
1856         *p = (XMLCh)fgetc(f);
1857         }
1858     fclose(f);
1859     charbuf[filelen] = '\0';
1862     /*
1863     printf("nrbytes:%d\n",wc_count);
1864     printf("buf:%ls\n======\n",charbuf);
1865     */
1866     Element *n = parse(charbuf, 0, filelen);
1867     delete[] charbuf;
1868     return n;
1873 //########################################################################
1874 //########################################################################
1875 //##  E N D    X M L
1876 //########################################################################
1877 //########################################################################
1880 //########################################################################
1881 //########################################################################
1882 //##  U R I
1883 //########################################################################
1884 //########################################################################
1886 //This would normally be a call to a UNICODE function
1887 #define isLetter(x) isalpha(x)
1889 /**
1890  *  A class that implements the W3C URI resource reference.
1891  */
1892 class URI
1894 public:
1896     typedef enum
1897         {
1898         SCHEME_NONE =0,
1899         SCHEME_DATA,
1900         SCHEME_HTTP,
1901         SCHEME_HTTPS,
1902         SCHEME_FTP,
1903         SCHEME_FILE,
1904         SCHEME_LDAP,
1905         SCHEME_MAILTO,
1906         SCHEME_NEWS,
1907         SCHEME_TELNET
1908         } SchemeTypes;
1910     /**
1911      *
1912      */
1913     URI()
1914         {
1915         init();
1916         }
1918     /**
1919      *
1920      */
1921     URI(const String &str)
1922         {
1923         init();
1924         parse(str);
1925         }
1928     /**
1929      *
1930      */
1931     URI(const char *str)
1932         {
1933         init();
1934         String domStr = str;
1935         parse(domStr);
1936         }
1939     /**
1940      *
1941      */
1942     URI(const URI &other)
1943         {
1944         init();
1945         assign(other);
1946         }
1949     /**
1950      *
1951      */
1952     URI &operator=(const URI &other)
1953         {
1954         init();
1955         assign(other);
1956         return *this;
1957         }
1960     /**
1961      *
1962      */
1963     virtual ~URI()
1964         {}
1968     /**
1969      *
1970      */
1971     virtual bool parse(const String &str);
1973     /**
1974      *
1975      */
1976     virtual String toString() const;
1978     /**
1979      *
1980      */
1981     virtual int getScheme() const;
1983     /**
1984      *
1985      */
1986     virtual String getSchemeStr() const;
1988     /**
1989      *
1990      */
1991     virtual String getAuthority() const;
1993     /**
1994      *  Same as getAuthority, but if the port has been specified
1995      *  as host:port , the port will not be included
1996      */
1997     virtual String getHost() const;
1999     /**
2000      *
2001      */
2002     virtual int getPort() const;
2004     /**
2005      *
2006      */
2007     virtual String getPath() const;
2009     /**
2010      *
2011      */
2012     virtual String getNativePath() const;
2014     /**
2015      *
2016      */
2017     virtual bool isAbsolute() const;
2019     /**
2020      *
2021      */
2022     virtual bool isOpaque() const;
2024     /**
2025      *
2026      */
2027     virtual String getQuery() const;
2029     /**
2030      *
2031      */
2032     virtual String getFragment() const;
2034     /**
2035      *
2036      */
2037     virtual URI resolve(const URI &other) const;
2039     /**
2040      *
2041      */
2042     virtual void normalize();
2044 private:
2046     /**
2047      *
2048      */
2049     void init()
2050         {
2051         parsebuf  = NULL;
2052         parselen  = 0;
2053         scheme    = SCHEME_NONE;
2054         schemeStr = "";
2055         port      = 0;
2056         authority = "";
2057         path      = "";
2058         absolute  = false;
2059         opaque    = false;
2060         query     = "";
2061         fragment  = "";
2062         }
2065     /**
2066      *
2067      */
2068     void assign(const URI &other)
2069         {
2070         scheme    = other.scheme;
2071         schemeStr = other.schemeStr;
2072         authority = other.authority;
2073         port      = other.port;
2074         path      = other.path;
2075         absolute  = other.absolute;
2076         opaque    = other.opaque;
2077         query     = other.query;
2078         fragment  = other.fragment;
2079         }
2081     int scheme;
2083     String schemeStr;
2085     String authority;
2087     bool portSpecified;
2089     int port;
2091     String path;
2093     bool absolute;
2095     bool opaque;
2097     String query;
2099     String fragment;
2101     void error(const char *fmt, ...);
2103     void trace(const char *fmt, ...);
2106     int peek(int p);
2108     int match(int p, char *key);
2110     int parseScheme(int p);
2112     int parseHierarchicalPart(int p0);
2114     int parseQuery(int p0);
2116     int parseFragment(int p0);
2118     int parse(int p);
2120     char *parsebuf;
2122     int parselen;
2124 };
2128 typedef struct
2130     int  ival;
2131     char *sval;
2132     int  port;
2133 } LookupEntry;
2135 LookupEntry schemes[] =
2137     { URI::SCHEME_DATA,   "data:",    0 },
2138     { URI::SCHEME_HTTP,   "http:",   80 },
2139     { URI::SCHEME_HTTPS,  "https:", 443 },
2140     { URI::SCHEME_FTP,    "ftp",     12 },
2141     { URI::SCHEME_FILE,   "file:",    0 },
2142     { URI::SCHEME_LDAP,   "ldap:",  123 },
2143     { URI::SCHEME_MAILTO, "mailto:", 25 },
2144     { URI::SCHEME_NEWS,   "news:",  117 },
2145     { URI::SCHEME_TELNET, "telnet:", 23 },
2146     { 0,                  NULL,       0 }
2147 };
2150 String URI::toString() const
2152     String str = schemeStr;
2153     if (authority.size() > 0)
2154         {
2155         str.append("//");
2156         str.append(authority);
2157         }
2158     str.append(path);
2159     if (query.size() > 0)
2160         {
2161         str.append("?");
2162         str.append(query);
2163         }
2164     if (fragment.size() > 0)
2165         {
2166         str.append("#");
2167         str.append(fragment);
2168         }
2169     return str;
2173 int URI::getScheme() const
2175     return scheme;
2178 String URI::getSchemeStr() const
2180     return schemeStr;
2184 String URI::getAuthority() const
2186     String ret = authority;
2187     if (portSpecified && port>=0)
2188         {
2189         char buf[7];
2190         snprintf(buf, 6, ":%6d", port);
2191         ret.append(buf);
2192         }
2193     return ret;
2196 String URI::getHost() const
2198     return authority;
2201 int URI::getPort() const
2203     return port;
2207 String URI::getPath() const
2209     return path;
2212 String URI::getNativePath() const
2214     String npath;
2215 #ifdef __WIN32__
2216     unsigned int firstChar = 0;
2217     if (path.size() >= 3)
2218         {
2219         if (path[0] == '/' &&
2220             isLetter(path[1]) &&
2221             path[2] == ':')
2222             firstChar++;
2223          }
2224     for (unsigned int i=firstChar ; i<path.size() ; i++)
2225         {
2226         XMLCh ch = (XMLCh) path[i];
2227         if (ch == '/')
2228             npath.push_back((XMLCh)'\\');
2229         else
2230             npath.push_back(ch);
2231         }
2232 #else
2233     npath = path;
2234 #endif
2235     return npath;
2239 bool URI::isAbsolute() const
2241     return absolute;
2244 bool URI::isOpaque() const
2246     return opaque;
2250 String URI::getQuery() const
2252     return query;
2256 String URI::getFragment() const
2258     return fragment;
2262 URI URI::resolve(const URI &other) const
2264     //### According to w3c, this is handled in 3 cases
2266     //## 1
2267     if (opaque || other.isAbsolute())
2268         return other;
2270     //## 2
2271     if (other.fragment.size()  >  0 &&
2272         other.path.size()      == 0 &&
2273         other.scheme           == SCHEME_NONE &&
2274         other.authority.size() == 0 &&
2275         other.query.size()     == 0 )
2276         {
2277         URI fragUri = *this;
2278         fragUri.fragment = other.fragment;
2279         return fragUri;
2280         }
2282     //## 3 http://www.ietf.org/rfc/rfc2396.txt, section 5.2
2283     URI newUri;
2284     //# 3.1
2285     newUri.scheme    = scheme;
2286     newUri.schemeStr = schemeStr;
2287     newUri.query     = other.query;
2288     newUri.fragment  = other.fragment;
2289     if (other.authority.size() > 0)
2290         {
2291         //# 3.2
2292         if (absolute || other.absolute)
2293             newUri.absolute = true;
2294         newUri.authority = other.authority;
2295         newUri.port      = other.port;//part of authority
2296         newUri.path      = other.path;
2297         }
2298     else
2299         {
2300         //# 3.3
2301         if (other.absolute)
2302             {
2303             newUri.absolute = true;
2304             newUri.path     = other.path;
2305             }
2306         else
2307             {
2308             unsigned int pos = path.find_last_of('/');
2309             if (pos != path.npos)
2310                 {
2311                 String tpath = path.substr(0, pos+1);
2312                 tpath.append(other.path);
2313                 newUri.path = tpath;
2314                 }
2315             else
2316                 newUri.path = other.path;
2317             }
2318         }
2320     newUri.normalize();
2321     return newUri;
2325 /**
2326  *  This follows the Java URI algorithm:
2327  *   1. All "." segments are removed.
2328  *   2. If a ".." segment is preceded by a non-".." segment
2329  *          then both of these segments are removed. This step
2330  *          is repeated until it is no longer applicable.
2331  *   3. If the path is relative, and if its first segment
2332  *          contains a colon character (':'), then a "." segment
2333  *          is prepended. This prevents a relative URI with a path
2334  *          such as "a:b/c/d" from later being re-parsed as an
2335  *          opaque URI with a scheme of "a" and a scheme-specific
2336  *          part of "b/c/d". (Deviation from RFC 2396)
2337  */
2338 void URI::normalize()
2340     std::vector<String> segments;
2342     //## Collect segments
2343     if (path.size()<2)
2344         return;
2345     bool abs = false;
2346     unsigned int pos=0;
2347     if (path[0]=='/')
2348         {
2349         abs = true;
2350         pos++;
2351         }
2352     while (pos < path.size())
2353         {
2354         unsigned int pos2 = path.find('/', pos);
2355         if (pos2==path.npos)
2356             {
2357             String seg = path.substr(pos);
2358             //printf("last segment:%s\n", seg.c_str());
2359             segments.push_back(seg);
2360             break;
2361             }
2362         if (pos2>pos)
2363             {
2364             String seg = path.substr(pos, pos2-pos);
2365             //printf("segment:%s\n", seg.c_str());
2366             segments.push_back(seg);
2367             }
2368         pos = pos2;
2369         pos++;
2370         }
2372     //## Clean up (normalize) segments
2373     bool edited = false;
2374     std::vector<String>::iterator iter;
2375     for (iter=segments.begin() ; iter!=segments.end() ; )
2376         {
2377         String s = *iter;
2378         if (s == ".")
2379             {
2380             iter = segments.erase(iter);
2381             edited = true;
2382             }
2383         else if (s == ".." &&
2384                  iter != segments.begin() &&
2385                  *(iter-1) != "..")
2386             {
2387             iter--; //back up, then erase two entries
2388             iter = segments.erase(iter);
2389             iter = segments.erase(iter);
2390             edited = true;
2391             }
2392         else
2393             iter++;
2394         }
2396     //## Rebuild path, if necessary
2397     if (edited)
2398         {
2399         path.clear();
2400         if (abs)
2401             {
2402             path.append("/");
2403             }
2404         std::vector<String>::iterator iter;
2405         for (iter=segments.begin() ; iter!=segments.end() ; iter++)
2406             {
2407             if (iter != segments.begin())
2408                 path.append("/");
2409             path.append(*iter);
2410             }
2411         }
2417 //#########################################################################
2418 //# M E S S A G E S
2419 //#########################################################################
2421 void URI::error(const char *fmt, ...)
2423     va_list args;
2424     fprintf(stderr, "URI error: ");
2425     va_start(args, fmt);
2426     vfprintf(stderr, fmt, args);
2427     va_end(args);
2428     fprintf(stderr, "\n");
2431 void URI::trace(const char *fmt, ...)
2433     va_list args;
2434     fprintf(stdout, "URI: ");
2435     va_start(args, fmt);
2436     vfprintf(stdout, fmt, args);
2437     va_end(args);
2438     fprintf(stdout, "\n");
2443 //#########################################################################
2444 //# P A R S I N G
2445 //#########################################################################
2449 int URI::peek(int p)
2451     if (p<0 || p>=parselen)
2452         return -1;
2453     return parsebuf[p];
2458 int URI::match(int p0, char *key)
2460     int p = p0;
2461     while (p < parselen)
2462         {
2463         if (*key == '\0')
2464             return p;
2465         else if (*key != parsebuf[p])
2466             break;
2467         p++; key++;
2468         }
2469     return p0;
2472 //#########################################################################
2473 //#  Parsing is performed according to:
2474 //#  http://www.gbiv.com/protocols/uri/rfc/rfc3986.html#components
2475 //#########################################################################
2477 int URI::parseScheme(int p0)
2479     int p = p0;
2480     for (LookupEntry *entry = schemes; entry->sval ; entry++)
2481         {
2482         int p2 = match(p, entry->sval);
2483         if (p2 > p)
2484             {
2485             schemeStr = entry->sval;
2486             scheme    = entry->ival;
2487             port      = entry->port;
2488             p = p2;
2489             return p;
2490             }
2491         }
2493     return p;
2497 int URI::parseHierarchicalPart(int p0)
2499     int p = p0;
2500     int ch;
2502     //# Authority field (host and port, for example)
2503     int p2 = match(p, "//");
2504     if (p2 > p)
2505         {
2506         p = p2;
2507         portSpecified = false;
2508         String portStr;
2509         while (p < parselen)
2510             {
2511             ch = peek(p);
2512             if (ch == '/')
2513                 break;
2514             else if (ch == ':')
2515                 portSpecified = true;
2516             else if (portSpecified)
2517                 portStr.push_back((XMLCh)ch);
2518             else
2519                 authority.push_back((XMLCh)ch);
2520             p++;
2521             }
2522         if (portStr.size() > 0)
2523             {
2524             char *pstr = (char *)portStr.c_str();
2525             char *endStr;
2526             long val = strtol(pstr, &endStr, 10);
2527             if (endStr > pstr) //successful parse?
2528                 port = val;
2529             }
2530         }
2532     //# Are we absolute?
2533     ch = peek(p);
2534     if (isLetter(ch) && peek(p+1)==':')
2535         {
2536         absolute = true;
2537         path.push_back((XMLCh)'/');
2538         }
2539     else if (ch == '/')
2540         {
2541         absolute = true;
2542         if (p>p0) //in other words, if '/' is not the first char
2543             opaque = true;
2544         path.push_back((XMLCh)ch);
2545         p++;
2546         }
2548     while (p < parselen)
2549         {
2550         ch = peek(p);
2551         if (ch == '?' || ch == '#')
2552             break;
2553         path.push_back((XMLCh)ch);
2554         p++;
2555         }
2557     return p;
2560 int URI::parseQuery(int p0)
2562     int p = p0;
2563     int ch = peek(p);
2564     if (ch != '?')
2565         return p0;
2567     p++;
2568     while (p < parselen)
2569         {
2570         ch = peek(p);
2571         if (ch == '#')
2572             break;
2573         query.push_back((XMLCh)ch);
2574         p++;
2575         }
2578     return p;
2581 int URI::parseFragment(int p0)
2584     int p = p0;
2585     int ch = peek(p);
2586     if (ch != '#')
2587         return p0;
2589     p++;
2590     while (p < parselen)
2591         {
2592         ch = peek(p);
2593         if (ch == '?')
2594             break;
2595         fragment.push_back((XMLCh)ch);
2596         p++;
2597         }
2600     return p;
2604 int URI::parse(int p0)
2607     int p = p0;
2609     int p2 = parseScheme(p);
2610     if (p2 < 0)
2611         {
2612         error("Scheme");
2613         return -1;
2614         }
2615     p = p2;
2618     p2 = parseHierarchicalPart(p);
2619     if (p2 < 0)
2620         {
2621         error("Hierarchical part");
2622         return -1;
2623         }
2624     p = p2;
2626     p2 = parseQuery(p);
2627     if (p2 < 0)
2628         {
2629         error("Query");
2630         return -1;
2631         }
2632     p = p2;
2635     p2 = parseFragment(p);
2636     if (p2 < 0)
2637         {
2638         error("Fragment");
2639         return -1;
2640         }
2641     p = p2;
2643     return p;
2649 bool URI::parse(const String &str)
2651     init();
2652     
2653     parselen = str.size();
2655     String tmp;
2656     for (unsigned int i=0 ; i<str.size() ; i++)
2657         {
2658         XMLCh ch = (XMLCh) str[i];
2659         if (ch == '\\')
2660             tmp.push_back((XMLCh)'/');
2661         else
2662             tmp.push_back(ch);
2663         }
2664     parsebuf = (char *) tmp.c_str();
2667     int p = parse(0);
2668     normalize();
2670     if (p < 0)
2671         {
2672         error("Syntax error");
2673         return false;
2674         }
2676     //printf("uri:%s\n", toString().c_str());
2677     //printf("path:%s\n", path.c_str());
2679     return true;
2690 //########################################################################
2691 //########################################################################
2692 //##  M A K E
2693 //########################################################################
2694 //########################################################################
2696 //########################################################################
2697 //# F I L E S E T
2698 //########################################################################
2699 /**
2700  * This is the descriptor for a <fileset> item
2701  */
2702 class FileSet
2704 public:
2706     /**
2707      *
2708      */
2709     FileSet()
2710         {}
2712     /**
2713      *
2714      */
2715     FileSet(const FileSet &other)
2716         { assign(other); }
2718     /**
2719      *
2720      */
2721     FileSet &operator=(const FileSet &other)
2722         { assign(other); return *this; }
2724     /**
2725      *
2726      */
2727     virtual ~FileSet()
2728         {}
2730     /**
2731      *
2732      */
2733     String getDirectory()
2734         { return directory; }
2735         
2736     /**
2737      *
2738      */
2739     void setDirectory(const String &val)
2740         { directory = val; }
2742     /**
2743      *
2744      */
2745     void setFiles(const std::vector<String> &val)
2746         { files = val; }
2748     /**
2749      *
2750      */
2751     std::vector<String> getFiles()
2752         { return files; }
2753         
2754     /**
2755      *
2756      */
2757     void setIncludes(const std::vector<String> &val)
2758         { includes = val; }
2760     /**
2761      *
2762      */
2763     std::vector<String> getIncludes()
2764         { return includes; }
2765         
2766     /**
2767      *
2768      */
2769     void setExcludes(const std::vector<String> &val)
2770         { excludes = val; }
2772     /**
2773      *
2774      */
2775     std::vector<String> getExcludes()
2776         { return excludes; }
2777         
2778     /**
2779      *
2780      */
2781     unsigned int size()
2782         { return files.size(); }
2783         
2784     /**
2785      *
2786      */
2787     String operator[](int index)
2788         { return files[index]; }
2789         
2790     /**
2791      *
2792      */
2793     void clear()
2794         {
2795         directory = "";
2796         files.clear();
2797         includes.clear();
2798         excludes.clear();
2799         }
2800         
2802 private:
2804     void assign(const FileSet &other)
2805         {
2806         directory = other.directory;
2807         files     = other.files;
2808         includes  = other.includes;
2809         excludes  = other.excludes;
2810         }
2812     String directory;
2813     std::vector<String> files;
2814     std::vector<String> includes;
2815     std::vector<String> excludes;
2816 };
2821 //########################################################################
2822 //# M A K E    B A S E
2823 //########################################################################
2824 /**
2825  * Base class for all classes in this file
2826  */
2827 class MakeBase
2829 public:
2830     MakeBase()
2831         {}
2832     virtual ~MakeBase()
2833         {}
2835     /**
2836      *     Return the URI of the file associated with this object 
2837      */     
2838     URI getURI()
2839         { return uri; }
2841     /**
2842      * Set the uri to the given string
2843      */
2844     void setURI(const String &uristr)
2845         { uri.parse(uristr); }
2847     /**
2848      *  Resolve another path relative to this one
2849      */
2850     String resolve(const String &otherPath);
2852     /**
2853      *  Get an element attribute, performing substitutions if necessary
2854      */
2855     bool getAttribute(Element *elem, const String &name, String &result);
2857     /**
2858      * Get an element value, performing substitutions if necessary
2859      */
2860     bool getValue(Element *elem, String &result);
2862 protected:
2864     /**
2865      *    The path to the file associated with this object
2866      */     
2867     URI uri;
2870     /**
2871      *  Print a printf()-like formatted error message
2872      */
2873     void error(char *fmt, ...);
2875     /**
2876      *  Print a printf()-like formatted trace message
2877      */
2878     void status(char *fmt, ...);
2880     /**
2881      *  Print a printf()-like formatted trace message
2882      */
2883     void trace(char *fmt, ...);
2885     /**
2886      *  Check if a given string matches a given regex pattern
2887      */
2888     bool regexMatch(const String &str, const String &pattern);
2890     /**
2891      *
2892      */
2893     String getSuffix(const String &fname);
2895     /**
2896      * Break up a string into substrings delimited the characters
2897      * in delimiters.  Null-length substrings are ignored
2898      */  
2899     std::vector<String> tokenize(const String &val,
2900                           const String &delimiters);
2902     /**
2903      *  replace runs of whitespace with a space
2904      */
2905     String strip(const String &s);
2907     /**
2908      *  remove leading whitespace from each line
2909      */
2910     String leftJustify(const String &s);
2912     /**
2913      *  remove leading and trailing whitespace from string
2914      */
2915     String trim(const String &s);
2917     /**
2918      * Return the native format of the canonical
2919      * path which we store
2920      */
2921     String getNativePath(const String &path);
2923     /**
2924      * Execute a shell command.  Outbuf is a ref to a string
2925      * to catch the result.     
2926      */         
2927     bool executeCommand(const String &call,
2928                         const String &inbuf,
2929                         String &outbuf,
2930                         String &errbuf);
2931     /**
2932      * List all directories in a given base and starting directory
2933      * It is usually called like:
2934      *        bool ret = listDirectories("src", "", result);    
2935      */         
2936     bool listDirectories(const String &baseName,
2937                          const String &dirname,
2938                          std::vector<String> &res);
2940     /**
2941      * Find all files in the named directory 
2942      */         
2943     bool listFiles(const String &baseName,
2944                    const String &dirname,
2945                    std::vector<String> &result);
2947     /**
2948      * Perform a listing for a fileset 
2949      */         
2950     bool listFiles(MakeBase &propRef, FileSet &fileSet);
2952     /**
2953      * Parse a <patternset>
2954      */  
2955     bool parsePatternSet(Element *elem,
2956                        MakeBase &propRef,
2957                        std::vector<String> &includes,
2958                        std::vector<String> &excludes);
2960     /**
2961      * Parse a <fileset> entry, and determine which files
2962      * should be included
2963      */  
2964     bool parseFileSet(Element *elem,
2965                     MakeBase &propRef,
2966                     FileSet &fileSet);
2968     /**
2969      * Return this object's property list
2970      */
2971     virtual std::map<String, String> &getProperties()
2972         { return properties; }
2974     /**
2975      * Return a named property if found, else a null string
2976      */
2977     virtual String getProperty(const String &name)
2978         {
2979         String val;
2980         std::map<String, String>::iterator iter;
2981         iter = properties.find(name);
2982         if (iter != properties.end())
2983             val = iter->second;
2984         return val;
2985         }
2988     std::map<String, String> properties;
2990     /**
2991      * Turn 'true' and 'false' into boolean values
2992      */             
2993     bool getBool(const String &str, bool &val);
2995     /**
2996      * Create a directory, making intermediate dirs
2997      * if necessary
2998      */                  
2999     bool createDirectory(const String &dirname);
3001     /**
3002      * Delete a directory and its children if desired
3003      */
3004     bool removeDirectory(const String &dirName);
3006     /**
3007      * Copy a file from one name to another. Perform only if needed
3008      */ 
3009     bool copyFile(const String &srcFile, const String &destFile);
3011     /**
3012      * Tests if the file exists and is a regular file
3013      */ 
3014     bool isRegularFile(const String &fileName);
3016     /**
3017      * Tests if the file exists and is a directory
3018      */ 
3019     bool isDirectory(const String &fileName);
3021     /**
3022      * Tests is the modification date of fileA is newer than fileB
3023      */ 
3024     bool isNewerThan(const String &fileA, const String &fileB);
3026 private:
3028     /**
3029      * replace variable refs like ${a} with their values
3030      */         
3031     bool getSubstitutions(const String &s, String &result);
3035 };
3040 /**
3041  *  Print a printf()-like formatted error message
3042  */
3043 void MakeBase::error(char *fmt, ...)
3045     va_list args;
3046     va_start(args,fmt);
3047     fprintf(stderr, "Make error: ");
3048     vfprintf(stderr, fmt, args);
3049     fprintf(stderr, "\n");
3050     va_end(args) ;
3055 /**
3056  *  Print a printf()-like formatted trace message
3057  */
3058 void MakeBase::status(char *fmt, ...)
3060     va_list args;
3061     va_start(args,fmt);
3062     //fprintf(stdout, " ");
3063     vfprintf(stdout, fmt, args);
3064     fprintf(stdout, "\n");
3065     va_end(args) ;
3070 /**
3071  *  Resolve another path relative to this one
3072  */
3073 String MakeBase::resolve(const String &otherPath)
3075     URI otherURI(otherPath);
3076     URI fullURI = uri.resolve(otherURI);
3077     String ret = fullURI.toString();
3078     return ret;
3082 /**
3083  *  Print a printf()-like formatted trace message
3084  */
3085 void MakeBase::trace(char *fmt, ...)
3087     va_list args;
3088     va_start(args,fmt);
3089     fprintf(stdout, "Make: ");
3090     vfprintf(stdout, fmt, args);
3091     fprintf(stdout, "\n");
3092     va_end(args) ;
3097 /**
3098  *  Check if a given string matches a given regex pattern
3099  */
3100 bool MakeBase::regexMatch(const String &str, const String &pattern)
3102     const TRexChar *terror = NULL;
3103     const TRexChar *cpat = pattern.c_str();
3104     TRex *expr = trex_compile(cpat, &terror);
3105     if (!expr)
3106         {
3107         if (!terror)
3108             terror = "undefined";
3109         error("compilation error [%s]!\n", terror);
3110         return false;
3111         } 
3113     bool ret = true;
3115     const TRexChar *cstr = str.c_str();
3116     if (trex_match(expr, cstr))
3117         {
3118         ret = true;
3119         }
3120     else
3121         {
3122         ret = false;
3123         }
3125     trex_free(expr);
3127     return ret;
3130 /**
3131  *  Return the suffix, if any, of a file name
3132  */
3133 String MakeBase::getSuffix(const String &fname)
3135     if (fname.size() < 2)
3136         return "";
3137     unsigned int pos = fname.find_last_of('.');
3138     if (pos == fname.npos)
3139         return "";
3140     pos++;
3141     String res = fname.substr(pos, fname.size()-pos);
3142     //trace("suffix:%s", res.c_str()); 
3143     return res;
3148 /**
3149  * Break up a string into substrings delimited the characters
3150  * in delimiters.  Null-length substrings are ignored
3151  */  
3152 std::vector<String> MakeBase::tokenize(const String &str,
3153                                 const String &delimiters)
3156     std::vector<String> res;
3157     char *del = (char *)delimiters.c_str();
3158     String dmp;
3159     for (unsigned int i=0 ; i<str.size() ; i++)
3160         {
3161         char ch = str[i];
3162         char *p = (char *)0;
3163         for (p=del ; *p ; p++)
3164             if (*p == ch)
3165                 break;
3166         if (*p)
3167             {
3168             if (dmp.size() > 0)
3169                 {
3170                 res.push_back(dmp);
3171                 dmp.clear();
3172                 }
3173             }
3174         else
3175             {
3176             dmp.push_back(ch);
3177             }
3178         }
3179     //Add tail
3180     if (dmp.size() > 0)
3181         {
3182         res.push_back(dmp);
3183         dmp.clear();
3184         }
3186     return res;
3191 /**
3192  *  replace runs of whitespace with a single space
3193  */
3194 String MakeBase::strip(const String &s)
3196     int len = s.size();
3197     String stripped;
3198     for (int i = 0 ; i<len ; i++)
3199         {
3200         char ch = s[i];
3201         if (isspace(ch))
3202             {
3203             stripped.push_back(' ');
3204             for ( ; i<len ; i++)
3205                 {
3206                 ch = s[i];
3207                 if (!isspace(ch))
3208                     {
3209                     stripped.push_back(ch);
3210                     break;
3211                     }
3212                 }
3213             }
3214         else
3215             {
3216             stripped.push_back(ch);
3217             }
3218         }
3219     return stripped;
3222 /**
3223  *  remove leading whitespace from each line
3224  */
3225 String MakeBase::leftJustify(const String &s)
3227     String out;
3228     int len = s.size();
3229     for (int i = 0 ; i<len ; )
3230         {
3231         char ch;
3232         //Skip to first visible character
3233         while (i<len)
3234             {
3235             ch = s[i];
3236             if (ch == '\n' || ch == '\r'
3237               || !isspace(ch))
3238                   break;
3239             i++;
3240             }
3241         //Copy the rest of the line
3242         while (i<len)
3243             {
3244             ch = s[i];
3245             if (ch == '\n' || ch == '\r')
3246                 {
3247                 if (ch != '\r')
3248                     out.push_back('\n');
3249                 i++;
3250                 break;
3251                 }
3252             else
3253                 {
3254                 out.push_back(ch);
3255                 }
3256             i++;
3257             }
3258         }
3259     return out;
3263 /**
3264  *  Removes whitespace from beginning and end of a string
3265  */
3266 String MakeBase::trim(const String &s)
3268     if (s.size() < 1)
3269         return s;
3270     
3271     //Find first non-ws char
3272     unsigned int begin = 0;
3273     for ( ; begin < s.size() ; begin++)
3274         {
3275         if (!isspace(s[begin]))
3276             break;
3277         }
3279     //Find first non-ws char, going in reverse
3280     unsigned int end = s.size() - 1;
3281     for ( ; end > begin ; end--)
3282         {
3283         if (!isspace(s[end]))
3284             break;
3285         }
3286     //trace("begin:%d  end:%d", begin, end);
3288     String res = s.substr(begin, end-begin+1);
3289     return res;
3292 /**
3293  * Return the native format of the canonical
3294  * path which we store
3295  */
3296 String MakeBase::getNativePath(const String &path)
3298 #ifdef __WIN32__
3299     String npath;
3300     unsigned int firstChar = 0;
3301     if (path.size() >= 3)
3302         {
3303         if (path[0] == '/' &&
3304             isalpha(path[1]) &&
3305             path[2] == ':')
3306             firstChar++;
3307         }
3308     for (unsigned int i=firstChar ; i<path.size() ; i++)
3309         {
3310         char ch = path[i];
3311         if (ch == '/')
3312             npath.push_back('\\');
3313         else
3314             npath.push_back(ch);
3315         }
3316     return npath;
3317 #else
3318     return path;
3319 #endif
3323 #ifdef __WIN32__
3324 #include <tchar.h>
3326 static String win32LastError()
3329     DWORD dw = GetLastError(); 
3331     LPVOID str;
3332     FormatMessage(
3333         FORMAT_MESSAGE_ALLOCATE_BUFFER | 
3334         FORMAT_MESSAGE_FROM_SYSTEM,
3335         NULL,
3336         dw,
3337         0,
3338         (LPTSTR) &str,
3339         0, NULL );
3340     LPTSTR p = _tcschr((const char *)str, _T('\r'));
3341     if(p != NULL)
3342         { // lose CRLF
3343         *p = _T('\0');
3344         }
3345     String ret = (char *)str;
3346     LocalFree(str);
3348     return ret;
3350 #endif
3354 /**
3355  * Execute a system call, using pipes to send data to the
3356  * program's stdin,  and reading stdout and stderr.
3357  */
3358 bool MakeBase::executeCommand(const String &command,
3359                               const String &inbuf,
3360                               String &outbuf,
3361                               String &errbuf)
3364     status("============ cmd ============\n%s\n=============================",
3365                 command.c_str());
3367     outbuf.clear();
3368     errbuf.clear();
3369     
3370 #ifdef __WIN32__
3372     /*
3373     I really hate having win32 code in this program, but the
3374     read buffer in command.com and cmd.exe are just too small
3375     for the large commands we need for compiling and linking.
3376     */
3378     bool ret = true;
3380     //# Allocate a separate buffer for safety
3381     char *paramBuf = new char[command.size() + 1];
3382     if (!paramBuf)
3383        {
3384        error("executeCommand cannot allocate command buffer");
3385        return false;
3386        }
3387     strcpy(paramBuf, (char *)command.c_str());
3389     //# Create pipes
3390     SECURITY_ATTRIBUTES saAttr; 
3391     saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); 
3392     saAttr.bInheritHandle = TRUE; 
3393     saAttr.lpSecurityDescriptor = NULL; 
3394     HANDLE stdinRead,  stdinWrite;
3395     HANDLE stdoutRead, stdoutWrite;
3396     HANDLE stderrRead, stderrWrite;
3397     if (!CreatePipe(&stdinRead, &stdinWrite, &saAttr, 0))
3398         {
3399         error("executeProgram: could not create pipe");
3400         delete[] paramBuf;
3401         return false;
3402         } 
3403     SetHandleInformation(stdinWrite, HANDLE_FLAG_INHERIT, 0);
3404     if (!CreatePipe(&stdoutRead, &stdoutWrite, &saAttr, 0))
3405         {
3406         error("executeProgram: could not create pipe");
3407         delete[] paramBuf;
3408         return false;
3409         } 
3410     SetHandleInformation(stdoutRead, HANDLE_FLAG_INHERIT, 0);
3411     if (!CreatePipe(&stderrRead, &stderrWrite, &saAttr, 0))
3412         {
3413         error("executeProgram: could not create pipe");
3414         delete[] paramBuf;
3415         return false;
3416         } 
3417     SetHandleInformation(stderrRead, HANDLE_FLAG_INHERIT, 0);
3419     // Create the process
3420     STARTUPINFO siStartupInfo;
3421     PROCESS_INFORMATION piProcessInfo;
3422     memset(&siStartupInfo, 0, sizeof(siStartupInfo));
3423     memset(&piProcessInfo, 0, sizeof(piProcessInfo));
3424     siStartupInfo.cb = sizeof(siStartupInfo);
3425     siStartupInfo.hStdError   =  stderrWrite;
3426     siStartupInfo.hStdOutput  =  stdoutWrite;
3427     siStartupInfo.hStdInput   =  stdinRead;
3428     siStartupInfo.dwFlags    |=  STARTF_USESTDHANDLES;
3429    
3430     if (!CreateProcess(NULL, paramBuf, NULL, NULL, true,
3431                 0, NULL, NULL, &siStartupInfo,
3432                 &piProcessInfo))
3433         {
3434         error("executeCommand : could not create process : %s",
3435                     win32LastError().c_str());
3436         ret = false;
3437         }
3439     DWORD bytesWritten;
3440     if (inbuf.size()>0 &&
3441         !WriteFile(stdinWrite, inbuf.c_str(), inbuf.size(), 
3442                &bytesWritten, NULL))
3443         {
3444         error("executeCommand: could not write to pipe");
3445         return false;
3446         }    
3447     if (!CloseHandle(stdinWrite))
3448         {          
3449         error("executeCommand: could not close write pipe");
3450         return false;
3451         }
3452     if (!CloseHandle(stdoutWrite))
3453         {
3454         error("executeCommand: could not close read pipe");
3455         return false;
3456         }
3457     if (!CloseHandle(stderrWrite))
3458         {
3459         error("executeCommand: could not close read pipe");
3460         return false;
3461         }
3462     while (true)
3463         {
3464         //trace("## stderr");
3465         DWORD avail;
3466         if (!PeekNamedPipe(stderrRead, NULL, 0, NULL, &avail, NULL))
3467             break;
3468         if (avail > 0)
3469             {
3470             DWORD bytesRead = 0;
3471             char readBuf[1025];
3472             if (avail>1024) avail = 1024;
3473             if (!ReadFile(stderrRead, readBuf, avail, &bytesRead, NULL)
3474                 || bytesRead == 0)
3475                 {
3476                 break;
3477                 }
3478             for (unsigned int i=0 ; i<bytesRead ; i++)
3479                 errbuf.push_back(readBuf[i]);
3480             }
3481         //trace("## stdout");
3482         if (!PeekNamedPipe(stdoutRead, NULL, 0, NULL, &avail, NULL))
3483             break;
3484         if (avail > 0)
3485             {
3486             DWORD bytesRead = 0;
3487             char readBuf[1025];
3488             if (avail>1024) avail = 1024;
3489             if (!ReadFile(stdoutRead, readBuf, avail, &bytesRead, NULL)
3490                 || bytesRead==0)
3491                 {
3492                 break;
3493                 }
3494             for (unsigned int i=0 ; i<bytesRead ; i++)
3495                 outbuf.push_back(readBuf[i]);
3496             }
3497         DWORD exitCode;
3498         GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
3499         if (exitCode != STILL_ACTIVE)
3500             break;
3501         Sleep(100);
3502         }    
3503     //trace("outbuf:%s", outbuf.c_str());
3504     if (!CloseHandle(stdoutRead))
3505         {
3506         error("executeCommand: could not close read pipe");
3507         return false;
3508         }
3509     if (!CloseHandle(stderrRead))
3510         {
3511         error("executeCommand: could not close read pipe");
3512         return false;
3513         }
3515     DWORD exitCode;
3516     GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
3517     //trace("exit code:%d", exitCode);
3518     if (exitCode != 0)
3519         {
3520         ret = false;
3521         }
3522     
3523     // Clean up
3524     CloseHandle(piProcessInfo.hProcess);
3525     CloseHandle(piProcessInfo.hThread);
3528     return ret;
3530 #else //do it unix-style
3532     String s;
3533     FILE *f = popen(command.c_str(), "r");
3534     int errnum = 0;
3535     if (f)
3536         {
3537         while (true)
3538             {
3539             int ch = fgetc(f);
3540             if (ch < 0)
3541                 break;
3542             s.push_back((char)ch);
3543             }
3544         errnum = pclose(f);
3545         }
3546     outbuf = s;
3547     if (errnum != 0)
3548         {
3549         error("exec of command '%s' failed : %s",
3550              command.c_str(), strerror(errno));
3551         return false;
3552         }
3553     else
3554         return true;
3556 #endif
3557
3562 bool MakeBase::listDirectories(const String &baseName,
3563                               const String &dirName,
3564                               std::vector<String> &res)
3566     res.push_back(dirName);
3567     String fullPath = baseName;
3568     if (dirName.size()>0)
3569         {
3570         fullPath.append("/");
3571         fullPath.append(dirName);
3572         }
3573     DIR *dir = opendir(fullPath.c_str());
3574     while (true)
3575         {
3576         struct dirent *de = readdir(dir);
3577         if (!de)
3578             break;
3580         //Get the directory member name
3581         String s = de->d_name;
3582         if (s.size() == 0 || s[0] == '.')
3583             continue;
3584         String childName = dirName;
3585         childName.append("/");
3586         childName.append(s);
3588         String fullChildPath = baseName;
3589         fullChildPath.append("/");
3590         fullChildPath.append(childName);
3591         struct stat finfo;
3592         String childNative = getNativePath(fullChildPath);
3593         if (stat(childNative.c_str(), &finfo)<0)
3594             {
3595             error("cannot stat file:%s", childNative.c_str());
3596             }
3597         else if (S_ISDIR(finfo.st_mode))
3598             {
3599             //trace("directory: %s", childName.c_str());
3600             if (!listDirectories(baseName, childName, res))
3601                 return false;
3602             }
3603         }
3604     closedir(dir);
3606     return true;
3610 bool MakeBase::listFiles(const String &baseDir,
3611                          const String &dirName,
3612                          std::vector<String> &res)
3614     String fullDir = baseDir;
3615     if (dirName.size()>0)
3616         {
3617         fullDir.append("/");
3618         fullDir.append(dirName);
3619         }
3620     String dirNative = getNativePath(fullDir);
3622     std::vector<String> subdirs;
3623     DIR *dir = opendir(dirNative.c_str());
3624     if (!dir)
3625         {
3626         error("Could not open directory %s : %s",
3627               dirNative.c_str(), strerror(errno));
3628         return false;
3629         }
3630     while (true)
3631         {
3632         struct dirent *de = readdir(dir);
3633         if (!de)
3634             break;
3636         //Get the directory member name
3637         String s = de->d_name;
3638         if (s.size() == 0 || s[0] == '.')
3639             continue;
3640         String childName;
3641         if (dirName.size()>0)
3642             {
3643             childName.append(dirName);
3644             childName.append("/");
3645             }
3646         childName.append(s);
3647         String fullChild = baseDir;
3648         fullChild.append("/");
3649         fullChild.append(childName);
3650         
3651         if (isDirectory(fullChild))
3652             {
3653             //trace("directory: %s", childName.c_str());
3654             if (!listFiles(baseDir, childName, res))
3655                 return false;
3656             continue;
3657             }
3658         else if (!isRegularFile(fullChild))
3659             {
3660             error("unknown file:%s", childName.c_str());
3661             return false;
3662             }
3664        //all done!
3665         res.push_back(childName);
3667         }
3668     closedir(dir);
3670     return true;
3674 bool MakeBase::listFiles(MakeBase &propRef, FileSet &fileSet)
3676     String baseDir = propRef.resolve(fileSet.getDirectory());
3677     std::vector<String> fileList;
3678     if (!listFiles(baseDir, "", fileList))
3679         return false;
3681     std::vector<String> includes = fileSet.getIncludes();
3682     std::vector<String> excludes = fileSet.getExcludes();
3684     std::vector<String> incs;
3685     std::vector<String>::iterator iter;
3687     std::sort(fileList.begin(), fileList.end());
3689     //If there are <includes>, then add files to the output
3690     //in the order of the include list
3691     if (includes.size()==0)
3692         incs = fileList;
3693     else
3694         {
3695         for (iter = includes.begin() ; iter != includes.end() ; iter++)
3696             {
3697             String pattern = *iter;
3698             std::vector<String>::iterator siter;
3699             for (siter = fileList.begin() ; siter != fileList.end() ; siter++)
3700                 {
3701                 String s = *siter;
3702                 if (regexMatch(s, pattern))
3703                     {
3704                     //trace("INCLUDED:%s", s.c_str());
3705                     incs.push_back(s);
3706                     }
3707                 }
3708             }
3709         }
3711     //Now trim off the <excludes>
3712     std::vector<String> res;
3713     for (iter = incs.begin() ; iter != incs.end() ; iter++)
3714         {
3715         String s = *iter;
3716         bool skipme = false;
3717         std::vector<String>::iterator siter;
3718         for (siter = excludes.begin() ; siter != excludes.end() ; siter++)
3719             {
3720             String pattern = *siter;
3721             if (regexMatch(s, pattern))
3722                 {
3723                 //trace("EXCLUDED:%s", s.c_str());
3724                 skipme = true;
3725                 break;
3726                 }
3727             }
3728         if (!skipme)
3729             res.push_back(s);
3730         }
3731         
3732     fileSet.setFiles(res);
3734     return true;
3741 bool MakeBase::getSubstitutions(const String &str, String &result)
3743     String s = trim(str);
3744     int len = (int)s.size();
3745     String val;
3746     for (int i=0 ; i<len ; i++)
3747         {
3748         char ch = s[i];
3749         if (ch == '$' && s[i+1] == '{')
3750             {
3751             String varname;
3752             int j = i+2;
3753             for ( ; j<len ; j++)
3754                 {
3755                 ch = s[j];
3756                 if (ch == '$' && s[j+1] == '{')
3757                     {
3758                     error("attribute %s cannot have nested variable references",
3759                            s.c_str());
3760                     return false;
3761                     }
3762                 else if (ch == '}')
3763                     {
3764                     std::map<String, String>::iterator iter;
3765                     iter = properties.find(trim(varname));
3766                     if (iter != properties.end())
3767                         {
3768                         val.append(iter->second);
3769                         }
3770                     else
3771                         {
3772                         error("property ${%s} not found", varname.c_str());
3773                         return false;
3774                         }
3775                     break;
3776                     }
3777                 else
3778                     {
3779                     varname.push_back(ch);
3780                     }
3781                 }
3782             i = j;
3783             }
3784         else
3785             {
3786             val.push_back(ch);
3787             }
3788         }
3789     result = val;
3790     return true;
3794 bool MakeBase::getAttribute(Element *elem, const String &name,
3795                                     String &result)
3797     String s = elem->getAttribute(name);
3798     return getSubstitutions(s, result);
3802 bool MakeBase::getValue(Element *elem, String &result)
3804     String s = elem->getValue();
3805     //Replace all runs of whitespace with a single space
3806     return getSubstitutions(s, result);
3810 /**
3811  * Turn 'true' and 'false' into boolean values
3812  */             
3813 bool MakeBase::getBool(const String &str, bool &val)
3815     if (str == "true")
3816         val = true;
3817     else if (str == "false")
3818         val = false;
3819     else
3820         {
3821         error("expected 'true' or 'false'.  found '%s'", str.c_str());
3822         return false;
3823         }
3824     return true;
3830 /**
3831  * Parse a <patternset> entry
3832  */  
3833 bool MakeBase::parsePatternSet(Element *elem,
3834                           MakeBase &propRef,
3835                           std::vector<String> &includes,
3836                           std::vector<String> &excludes
3837                           )
3839     std::vector<Element *> children  = elem->getChildren();
3840     for (unsigned int i=0 ; i<children.size() ; i++)
3841         {
3842         Element *child = children[i];
3843         String tagName = child->getName();
3844         if (tagName == "exclude")
3845             {
3846             String fname;
3847             if (!propRef.getAttribute(child, "name", fname))
3848                 return false;
3849             //trace("EXCLUDE: %s", fname.c_str());
3850             excludes.push_back(fname);
3851             }
3852         else if (tagName == "include")
3853             {
3854             String fname;
3855             if (!propRef.getAttribute(child, "name", fname))
3856                 return false;
3857             //trace("INCLUDE: %s", fname.c_str());
3858             includes.push_back(fname);
3859             }
3860         }
3862     return true;
3868 /**
3869  * Parse a <fileset> entry, and determine which files
3870  * should be included
3871  */  
3872 bool MakeBase::parseFileSet(Element *elem,
3873                           MakeBase &propRef,
3874                           FileSet &fileSet)
3876     String name = elem->getName();
3877     if (name != "fileset")
3878         {
3879         error("expected <fileset>");
3880         return false;
3881         }
3884     std::vector<String> includes;
3885     std::vector<String> excludes;
3887     //A fileset has one implied patternset
3888     if (!parsePatternSet(elem, propRef, includes, excludes))
3889         {
3890         return false;
3891         }
3892     //Look for child tags, including more patternsets
3893     std::vector<Element *> children  = elem->getChildren();
3894     for (unsigned int i=0 ; i<children.size() ; i++)
3895         {
3896         Element *child = children[i];
3897         String tagName = child->getName();
3898         if (tagName == "patternset")
3899             {
3900             if (!parsePatternSet(child, propRef, includes, excludes))
3901                 {
3902                 return false;
3903                 }
3904             }
3905         }
3907     String dir;
3908     //Now do the stuff
3909     //Get the base directory for reading file names
3910     if (!propRef.getAttribute(elem, "dir", dir))
3911         return false;
3913     fileSet.setDirectory(dir);
3914     fileSet.setIncludes(includes);
3915     fileSet.setExcludes(excludes);
3916     
3917     /*
3918     std::vector<String> fileList;
3919     if (dir.size() > 0)
3920         {
3921         String baseDir = propRef.resolve(dir);
3922         if (!listFiles(baseDir, "", includes, excludes, fileList))
3923             return false;
3924         }
3925     std::sort(fileList.begin(), fileList.end());
3926     result = fileList;
3927     */
3929     
3930     /*
3931     for (unsigned int i=0 ; i<result.size() ; i++)
3932         {
3933         trace("RES:%s", result[i].c_str());
3934         }
3935     */
3937     
3938     return true;
3943 /**
3944  * Create a directory, making intermediate dirs
3945  * if necessary
3946  */                  
3947 bool MakeBase::createDirectory(const String &dirname)
3949     //trace("## createDirectory: %s", dirname.c_str());
3950     //## first check if it exists
3951     struct stat finfo;
3952     String nativeDir = getNativePath(dirname);
3953     char *cnative = (char *) nativeDir.c_str();
3954 #ifdef __WIN32__
3955     if (strlen(cnative)==2 && cnative[1]==':')
3956         return true;
3957 #endif
3958     if (stat(cnative, &finfo)==0)
3959         {
3960         if (!S_ISDIR(finfo.st_mode))
3961             {
3962             error("mkdir: file %s exists but is not a directory",
3963                   cnative);
3964             return false;
3965             }
3966         else //exists
3967             {
3968             return true;
3969             }
3970         }
3972     //## 2: pull off the last path segment, if any,
3973     //## to make the dir 'above' this one, if necessary
3974     unsigned int pos = dirname.find_last_of('/');
3975     if (pos>0 && pos != dirname.npos)
3976         {
3977         String subpath = dirname.substr(0, pos);
3978         //A letter root (c:) ?
3979         if (!createDirectory(subpath))
3980             return false;
3981         }
3982         
3983     //## 3: now make
3984 #ifdef __WIN32__
3985     if (mkdir(cnative)<0)
3986 #else
3987     if (mkdir(cnative, S_IRWXU | S_IRWXG | S_IRWXO)<0)
3988 #endif
3989         {
3990         error("cannot make directory '%s' : %s",
3991                  cnative, strerror(errno));
3992         return false;
3993         }
3994         
3995     return true;
3999 /**
4000  * Remove a directory recursively
4001  */ 
4002 bool MakeBase::removeDirectory(const String &dirName)
4004     char *dname = (char *)dirName.c_str();
4006     DIR *dir = opendir(dname);
4007     if (!dir)
4008         {
4009         //# Let this fail nicely.
4010         return true;
4011         //error("error opening directory %s : %s", dname, strerror(errno));
4012         //return false;
4013         }
4014     
4015     while (true)
4016         {
4017         struct dirent *de = readdir(dir);
4018         if (!de)
4019             break;
4021         //Get the directory member name
4022         String s = de->d_name;
4023         if (s.size() == 0 || s[0] == '.')
4024             continue;
4025         String childName;
4026         if (dirName.size() > 0)
4027             {
4028             childName.append(dirName);
4029             childName.append("/");
4030             }
4031         childName.append(s);
4034         struct stat finfo;
4035         String childNative = getNativePath(childName);
4036         char *cnative = (char *)childNative.c_str();
4037         if (stat(cnative, &finfo)<0)
4038             {
4039             error("cannot stat file:%s", cnative);
4040             }
4041         else if (S_ISDIR(finfo.st_mode))
4042             {
4043             //trace("DEL dir: %s", childName.c_str());
4044             if (!removeDirectory(childName))
4045                 {
4046                 return false;
4047                 }
4048             }
4049         else if (!S_ISREG(finfo.st_mode))
4050             {
4051             //trace("not regular: %s", cnative);
4052             }
4053         else
4054             {
4055             //trace("DEL file: %s", childName.c_str());
4056             if (remove(cnative)<0)
4057                 {
4058                 error("error deleting %s : %s",
4059                      cnative, strerror(errno));
4060                 return false;
4061                 }
4062             }
4063         }
4064     closedir(dir);
4066     //Now delete the directory
4067     String native = getNativePath(dirName);
4068     if (rmdir(native.c_str())<0)
4069         {
4070         error("could not delete directory %s : %s",
4071             native.c_str() , strerror(errno));
4072         return false;
4073         }
4075     return true;
4076     
4080 /**
4081  * Copy a file from one name to another. Perform only if needed
4082  */ 
4083 bool MakeBase::copyFile(const String &srcFile, const String &destFile)
4085     //# 1 Check up-to-date times
4086     String srcNative = getNativePath(srcFile);
4087     struct stat srcinfo;
4088     if (stat(srcNative.c_str(), &srcinfo)<0)
4089         {
4090         error("source file %s for copy does not exist",
4091                  srcNative.c_str());
4092         return false;
4093         }
4095     String destNative = getNativePath(destFile);
4096     struct stat destinfo;
4097     if (stat(destNative.c_str(), &destinfo)==0)
4098         {
4099         if (destinfo.st_mtime >= srcinfo.st_mtime)
4100             return true;
4101         }
4102         
4103     //# 2 prepare a destination directory if necessary
4104     unsigned int pos = destFile.find_last_of('/');
4105     if (pos != destFile.npos)
4106         {
4107         String subpath = destFile.substr(0, pos);
4108         if (!createDirectory(subpath))
4109             return false;
4110         }
4112     //# 3 do the data copy
4113 #ifndef __WIN32__
4115     FILE *srcf = fopen(srcNative.c_str(), "rb");
4116     if (!srcf)
4117         {
4118         error("copyFile cannot open '%s' for reading", srcNative.c_str());
4119         return false;
4120         }
4121     FILE *destf = fopen(destNative.c_str(), "wb");
4122     if (!destf)
4123         {
4124         error("copyFile cannot open %s for writing", srcNative.c_str());
4125         return false;
4126         }
4128     while (!feof(srcf))
4129         {
4130         int ch = fgetc(srcf);
4131         if (ch<0)
4132             break;
4133         fputc(ch, destf);
4134         }
4136     fclose(destf);
4137     fclose(srcf);
4139 #else
4140     
4141     if (!CopyFile(srcNative.c_str(), destNative.c_str(), false))
4142         {
4143         error("copyFile from %s to %s failed",
4144              srcNative.c_str(), destNative.c_str());
4145         return false;
4146         }
4147         
4148 #endif /* __WIN32__ */
4151     return true;
4156 /**
4157  * Tests if the file exists and is a regular file
4158  */ 
4159 bool MakeBase::isRegularFile(const String &fileName)
4161     String native = getNativePath(fileName);
4162     struct stat finfo;
4163     
4164     //Exists?
4165     if (stat(native.c_str(), &finfo)<0)
4166         return false;
4169     //check the file mode
4170     if (!S_ISREG(finfo.st_mode))
4171         return false;
4173     return true;
4176 /**
4177  * Tests if the file exists and is a directory
4178  */ 
4179 bool MakeBase::isDirectory(const String &fileName)
4181     String native = getNativePath(fileName);
4182     struct stat finfo;
4183     
4184     //Exists?
4185     if (stat(native.c_str(), &finfo)<0)
4186         return false;
4189     //check the file mode
4190     if (!S_ISDIR(finfo.st_mode))
4191         return false;
4193     return true;
4198 /**
4199  * Tests is the modification of fileA is newer than fileB
4200  */ 
4201 bool MakeBase::isNewerThan(const String &fileA, const String &fileB)
4203     //trace("isNewerThan:'%s' , '%s'", fileA.c_str(), fileB.c_str());
4204     String nativeA = getNativePath(fileA);
4205     struct stat infoA;
4206     //IF source does not exist, NOT newer
4207     if (stat(nativeA.c_str(), &infoA)<0)
4208         {
4209         return false;
4210         }
4212     String nativeB = getNativePath(fileB);
4213     struct stat infoB;
4214     //IF dest does not exist, YES, newer
4215     if (stat(nativeB.c_str(), &infoB)<0)
4216         {
4217         return true;
4218         }
4220     //check the actual times
4221     if (infoA.st_mtime > infoB.st_mtime)
4222         {
4223         return true;
4224         }
4226     return false;
4230 //########################################################################
4231 //# P K G    C O N F I G
4232 //########################################################################
4234 /**
4235  *
4236  */
4237 class PkgConfig : public MakeBase
4240 public:
4242     /**
4243      *
4244      */
4245     PkgConfig()
4246         { init(); }
4248     /**
4249      *
4250      */
4251     PkgConfig(const String &namearg)
4252         { init(); name = namearg; }
4254     /**
4255      *
4256      */
4257     PkgConfig(const PkgConfig &other)
4258         { assign(other); }
4260     /**
4261      *
4262      */
4263     PkgConfig &operator=(const PkgConfig &other)
4264         { assign(other); return *this; }
4266     /**
4267      *
4268      */
4269     virtual ~PkgConfig()
4270         { }
4272     /**
4273      *
4274      */
4275     virtual String getName()
4276         { return name; }
4278     /**
4279      *
4280      */
4281     virtual String getDescription()
4282         { return description; }
4284     /**
4285      *
4286      */
4287     virtual String getCflags()
4288         { return cflags; }
4290     /**
4291      *
4292      */
4293     virtual String getLibs()
4294         { return libs; }
4296     /**
4297      *
4298      */
4299     virtual String getVersion()
4300         { return version; }
4302     /**
4303      *
4304      */
4305     virtual int getMajorVersion()
4306         { return majorVersion; }
4308     /**
4309      *
4310      */
4311     virtual int getMinorVersion()
4312         { return minorVersion; }
4314     /**
4315      *
4316      */
4317     virtual int getMicroVersion()
4318         { return microVersion; }
4320     /**
4321      *
4322      */
4323     virtual std::map<String, String> &getAttributes()
4324         { return attrs; }
4326     /**
4327      *
4328      */
4329     virtual std::vector<String> &getRequireList()
4330         { return requireList; }
4332     virtual bool readFile(const String &fileName);
4334 private:
4336     void init()
4337         {
4338         name         = "";
4339         description  = "";
4340         cflags       = "";
4341         libs         = "";
4342         requires     = "";
4343         version      = "";
4344         majorVersion = 0;
4345         minorVersion = 0;
4346         microVersion = 0;
4347         fileName     = "";
4348         attrs.clear();
4349         requireList.clear();
4350         }
4352     void assign(const PkgConfig &other)
4353         {
4354         name         = other.name;
4355         description  = other.description;
4356         cflags       = other.cflags;
4357         libs         = other.libs;
4358         requires     = other.requires;
4359         version      = other.version;
4360         majorVersion = other.majorVersion;
4361         minorVersion = other.minorVersion;
4362         microVersion = other.microVersion;
4363         fileName     = other.fileName;
4364         attrs        = other.attrs;
4365         requireList  = other.requireList;
4366         }
4370     int get(int pos);
4372     int skipwhite(int pos);
4374     int getword(int pos, String &ret);
4376     void parseRequires();
4378     void parseVersion();
4380     bool parse(const String &buf);
4382     void dumpAttrs();
4384     String name;
4386     String description;
4388     String cflags;
4390     String libs;
4392     String requires;
4394     String version;
4396     int majorVersion;
4398     int minorVersion;
4400     int microVersion;
4402     String fileName;
4404     std::map<String, String> attrs;
4406     std::vector<String> requireList;
4408     char *parsebuf;
4409     int parselen;
4410 };
4413 /**
4414  * Get a character from the buffer at pos.  If out of range,
4415  * return -1 for safety
4416  */
4417 int PkgConfig::get(int pos)
4419     if (pos>parselen)
4420         return -1;
4421     return parsebuf[pos];
4426 /**
4427  *  Skip over all whitespace characters beginning at pos.  Return
4428  *  the position of the first non-whitespace character.
4429  */
4430 int PkgConfig::skipwhite(int pos)
4432     while (pos < parselen)
4433         {
4434         int ch = get(pos);
4435         if (ch < 0)
4436             break;
4437         if (!isspace(ch))
4438             break;
4439         pos++;
4440         }
4441     return pos;
4445 /**
4446  *  Parse the buffer beginning at pos, for a word.  Fill
4447  *  'ret' with the result.  Return the position after the
4448  *  word.
4449  */
4450 int PkgConfig::getword(int pos, String &ret)
4452     while (pos < parselen)
4453         {
4454         int ch = get(pos);
4455         if (ch < 0)
4456             break;
4457         if (!isalnum(ch) && ch != '_' && ch != '-'&& ch != '.')
4458             break;
4459         ret.push_back((char)ch);
4460         pos++;
4461         }
4462     return pos;
4465 void PkgConfig::parseRequires()
4467     if (requires.size() == 0)
4468         return;
4469     parsebuf = (char *)requires.c_str();
4470     parselen = requires.size();
4471     int pos = 0;
4472     while (pos < parselen)
4473         {
4474         pos = skipwhite(pos);
4475         String val;
4476         int pos2 = getword(pos, val);
4477         if (pos2 == pos)
4478             break;
4479         pos = pos2;
4480         //trace("val %s", val.c_str());
4481         requireList.push_back(val);
4482         }
4485 static int getint(const String str)
4487     char *s = (char *)str.c_str();
4488     char *ends = NULL;
4489     long val = strtol(s, &ends, 10);
4490     if (ends == s)
4491         return 0L;
4492     else
4493         return val;
4496 void PkgConfig::parseVersion()
4498     if (version.size() == 0)
4499         return;
4500     String s1, s2, s3;
4501     unsigned int pos = 0;
4502     unsigned int pos2 = version.find('.', pos);
4503     if (pos2 == version.npos)
4504         {
4505         s1 = version;
4506         }
4507     else
4508         {
4509         s1 = version.substr(pos, pos2-pos);
4510         pos = pos2;
4511         pos++;
4512         if (pos < version.size())
4513             {
4514             pos2 = version.find('.', pos);
4515             if (pos2 == version.npos)
4516                 {
4517                 s2 = version.substr(pos, version.size()-pos);
4518                 }
4519             else
4520                 {
4521                 s2 = version.substr(pos, pos2-pos);
4522                 pos = pos2;
4523                 pos++;
4524                 if (pos < version.size())
4525                     s3 = version.substr(pos, pos2-pos);
4526                 }
4527             }
4528         }
4530     majorVersion = getint(s1);
4531     minorVersion = getint(s2);
4532     microVersion = getint(s3);
4533     //trace("version:%d.%d.%d", majorVersion,
4534     //          minorVersion, microVersion );
4538 bool PkgConfig::parse(const String &buf)
4540     init();
4542     parsebuf = (char *)buf.c_str();
4543     parselen = buf.size();
4544     int pos = 0;
4547     while (pos < parselen)
4548         {
4549         String attrName;
4550         pos = skipwhite(pos);
4551         int ch = get(pos);
4552         if (ch == '#')
4553             {
4554             //comment.  eat the rest of the line
4555             while (pos < parselen)
4556                 {
4557                 ch = get(pos);
4558                 if (ch == '\n' || ch < 0)
4559                     break;
4560                 pos++;
4561                 }
4562             continue;
4563             }
4564         pos = getword(pos, attrName);
4565         if (attrName.size() == 0)
4566             continue;
4567         pos = skipwhite(pos);
4568         ch = get(pos);
4569         if (ch != ':' && ch != '=')
4570             {
4571             error("expected ':' or '='");
4572             return false;
4573             }
4574         pos++;
4575         pos = skipwhite(pos);
4576         String attrVal;
4577         while (pos < parselen)
4578             {
4579             ch = get(pos);
4580             if (ch == '\n' || ch < 0)
4581                 break;
4582             else if (ch == '$' && get(pos+1) == '{')
4583                 {
4584                 //#  this is a ${substitution}
4585                 pos += 2;
4586                 String subName;
4587                 while (pos < parselen)
4588                     {
4589                     ch = get(pos);
4590                     if (ch < 0)
4591                         {
4592                         error("unterminated substitution");
4593                         return false;
4594                         }
4595                     else if (ch == '}')
4596                         break;
4597                     else
4598                         subName.push_back((char)ch);
4599                     pos++;
4600                     }
4601                 //trace("subName:%s", subName.c_str());
4602                 String subVal = attrs[subName];
4603                 //trace("subVal:%s", subVal.c_str());
4604                 attrVal.append(subVal);
4605                 }
4606             else
4607                 attrVal.push_back((char)ch);
4608             pos++;
4609             }
4611         attrVal = trim(attrVal);
4612         attrs[attrName] = attrVal;
4614         if (attrName == "Name")
4615             name = attrVal;
4616         else if (attrName == "Description")
4617             description = attrVal;
4618         else if (attrName == "Cflags")
4619             cflags = attrVal;
4620         else if (attrName == "Libs")
4621             libs = attrVal;
4622         else if (attrName == "Requires")
4623             requires = attrVal;
4624         else if (attrName == "Version")
4625             version = attrVal;
4627         //trace("name:'%s'  value:'%s'",
4628         //      attrName.c_str(), attrVal.c_str());
4629         }
4632     parseRequires();
4633     parseVersion();
4635     return true;
4638 void PkgConfig::dumpAttrs()
4640     //trace("### PkgConfig attributes for %s", fileName.c_str());
4641     std::map<String, String>::iterator iter;
4642     for (iter=attrs.begin() ; iter!=attrs.end() ; iter++)
4643         {
4644         trace("   %s = %s", iter->first.c_str(), iter->second.c_str());
4645         }
4649 bool PkgConfig::readFile(const String &fileNameArg)
4651     fileName = fileNameArg;
4653     FILE *f = fopen(fileName.c_str(), "r");
4654     if (!f)
4655         {
4656         error("cannot open file '%s' for reading", fileName.c_str());
4657         return false;
4658         }
4659     String buf;
4660     while (true)
4661         {
4662         int ch = fgetc(f);
4663         if (ch < 0)
4664             break;
4665         buf.push_back((char)ch);
4666         }
4667     fclose(f);
4669     //trace("####### File:\n%s", buf.c_str());
4670     if (!parse(buf))
4671         {
4672         return false;
4673         }
4675     dumpAttrs();
4677     return true;
4684 //########################################################################
4685 //# D E P T O O L
4686 //########################################################################
4690 /**
4691  *  Class which holds information for each file.
4692  */
4693 class FileRec
4695 public:
4697     typedef enum
4698         {
4699         UNKNOWN,
4700         CFILE,
4701         HFILE,
4702         OFILE
4703         } FileType;
4705     /**
4706      *  Constructor
4707      */
4708     FileRec()
4709         {init(); type = UNKNOWN;}
4711     /**
4712      *  Copy constructor
4713      */
4714     FileRec(const FileRec &other)
4715         {init(); assign(other);}
4716     /**
4717      *  Constructor
4718      */
4719     FileRec(int typeVal)
4720         {init(); type = typeVal;}
4721     /**
4722      *  Assignment operator
4723      */
4724     FileRec &operator=(const FileRec &other)
4725         {init(); assign(other); return *this;}
4728     /**
4729      *  Destructor
4730      */
4731     ~FileRec()
4732         {}
4734     /**
4735      *  Directory part of the file name
4736      */
4737     String path;
4739     /**
4740      *  Base name, sans directory and suffix
4741      */
4742     String baseName;
4744     /**
4745      *  File extension, such as cpp or h
4746      */
4747     String suffix;
4749     /**
4750      *  Type of file: CFILE, HFILE, OFILE
4751      */
4752     int type;
4754     /**
4755      * Used to list files ref'd by this one
4756      */
4757     std::map<String, FileRec *> files;
4760 private:
4762     void init()
4763         {
4764         }
4766     void assign(const FileRec &other)
4767         {
4768         type     = other.type;
4769         baseName = other.baseName;
4770         suffix   = other.suffix;
4771         files    = other.files;
4772         }
4774 };
4778 /**
4779  *  Simpler dependency record
4780  */
4781 class DepRec
4783 public:
4785     /**
4786      *  Constructor
4787      */
4788     DepRec()
4789         {init();}
4791     /**
4792      *  Copy constructor
4793      */
4794     DepRec(const DepRec &other)
4795         {init(); assign(other);}
4796     /**
4797      *  Constructor
4798      */
4799     DepRec(const String &fname)
4800         {init(); name = fname; }
4801     /**
4802      *  Assignment operator
4803      */
4804     DepRec &operator=(const DepRec &other)
4805         {init(); assign(other); return *this;}
4808     /**
4809      *  Destructor
4810      */
4811     ~DepRec()
4812         {}
4814     /**
4815      *  Directory part of the file name
4816      */
4817     String path;
4819     /**
4820      *  Base name, without the path and suffix
4821      */
4822     String name;
4824     /**
4825      *  Suffix of the source
4826      */
4827     String suffix;
4830     /**
4831      * Used to list files ref'd by this one
4832      */
4833     std::vector<String> files;
4836 private:
4838     void init()
4839         {
4840         }
4842     void assign(const DepRec &other)
4843         {
4844         path     = other.path;
4845         name     = other.name;
4846         suffix   = other.suffix;
4847         files    = other.files;
4848         }
4850 };
4853 class DepTool : public MakeBase
4855 public:
4857     /**
4858      *  Constructor
4859      */
4860     DepTool()
4861         {init();}
4863     /**
4864      *  Copy constructor
4865      */
4866     DepTool(const DepTool &other)
4867         {init(); assign(other);}
4869     /**
4870      *  Assignment operator
4871      */
4872     DepTool &operator=(const DepTool &other)
4873         {init(); assign(other); return *this;}
4876     /**
4877      *  Destructor
4878      */
4879     ~DepTool()
4880         {}
4883     /**
4884      *  Reset this section of code
4885      */
4886     virtual void init();
4887     
4888     /**
4889      *  Reset this section of code
4890      */
4891     virtual void assign(const DepTool &other)
4892         {
4893         }
4894     
4895     /**
4896      *  Sets the source directory which will be scanned
4897      */
4898     virtual void setSourceDirectory(const String &val)
4899         { sourceDir = val; }
4901     /**
4902      *  Returns the source directory which will be scanned
4903      */
4904     virtual String getSourceDirectory()
4905         { return sourceDir; }
4907     /**
4908      *  Sets the list of files within the directory to analyze
4909      */
4910     virtual void setFileList(const std::vector<String> &list)
4911         { fileList = list; }
4913     /**
4914      * Creates the list of all file names which will be
4915      * candidates for further processing.  Reads make.exclude
4916      * to see which files for directories to leave out.
4917      */
4918     virtual bool createFileList();
4921     /**
4922      *  Generates the forward dependency list
4923      */
4924     virtual bool generateDependencies();
4927     /**
4928      *  Generates the forward dependency list, saving the file
4929      */
4930     virtual bool generateDependencies(const String &);
4933     /**
4934      *  Load a dependency file
4935      */
4936     std::vector<DepRec> loadDepFile(const String &fileName);
4938     /**
4939      *  Load a dependency file, generating one if necessary
4940      */
4941     std::vector<DepRec> getDepFile(const String &fileName,
4942               bool forceRefresh);
4944     /**
4945      *  Save a dependency file
4946      */
4947     bool saveDepFile(const String &fileName);
4950 private:
4953     /**
4954      *
4955      */
4956     void parseName(const String &fullname,
4957                    String &path,
4958                    String &basename,
4959                    String &suffix);
4961     /**
4962      *
4963      */
4964     int get(int pos);
4966     /**
4967      *
4968      */
4969     int skipwhite(int pos);
4971     /**
4972      *
4973      */
4974     int getword(int pos, String &ret);
4976     /**
4977      *
4978      */
4979     bool sequ(int pos, char *key);
4981     /**
4982      *
4983      */
4984     bool addIncludeFile(FileRec *frec, const String &fname);
4986     /**
4987      *
4988      */
4989     bool scanFile(const String &fname, FileRec *frec);
4991     /**
4992      *
4993      */
4994     bool processDependency(FileRec *ofile,
4995                            FileRec *include,
4996                            int depth);
4998     /**
4999      *
5000      */
5001     String sourceDir;
5003     /**
5004      *
5005      */
5006     std::vector<String> fileList;
5008     /**
5009      *
5010      */
5011     std::vector<String> directories;
5013     /**
5014      * A list of all files which will be processed for
5015      * dependencies.  This is the only list that has the actual
5016      * records.  All other lists have pointers to these records.     
5017      */
5018     std::map<String, FileRec *> allFiles;
5020     /**
5021      * The list of .o files, and the
5022      * dependencies upon them.
5023      */
5024     std::map<String, FileRec *> depFiles;
5026     int depFileSize;
5027     char *depFileBuf;
5029     static const int readBufSize = 8192;
5030     char readBuf[8193];//byte larger
5032 };
5038 /**
5039  *  Clean up after processing.  Called by the destructor, but should
5040  *  also be called before the object is reused.
5041  */
5042 void DepTool::init()
5044     sourceDir = ".";
5046     fileList.clear();
5047     directories.clear();
5048     
5049     //clear refs
5050     depFiles.clear();
5051     //clear records
5052     std::map<String, FileRec *>::iterator iter;
5053     for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5054          delete iter->second;
5056     allFiles.clear(); 
5063 /**
5064  *  Parse a full path name into path, base name, and suffix
5065  */
5066 void DepTool::parseName(const String &fullname,
5067                         String &path,
5068                         String &basename,
5069                         String &suffix)
5071     if (fullname.size() < 2)
5072         return;
5074     unsigned int pos = fullname.find_last_of('/');
5075     if (pos != fullname.npos && pos<fullname.size()-1)
5076         {
5077         path = fullname.substr(0, pos);
5078         pos++;
5079         basename = fullname.substr(pos, fullname.size()-pos);
5080         }
5081     else
5082         {
5083         path = "";
5084         basename = fullname;
5085         }
5087     pos = basename.find_last_of('.');
5088     if (pos != basename.npos && pos<basename.size()-1)
5089         {
5090         suffix   = basename.substr(pos+1, basename.size()-pos-1);
5091         basename = basename.substr(0, pos);
5092         }
5094     //trace("parsename:%s %s %s", path.c_str(),
5095     //        basename.c_str(), suffix.c_str()); 
5100 /**
5101  *  Generate our internal file list.
5102  */
5103 bool DepTool::createFileList()
5106     for (unsigned int i=0 ; i<fileList.size() ; i++)
5107         {
5108         String fileName = fileList[i];
5109         //trace("## FileName:%s", fileName.c_str());
5110         String path;
5111         String basename;
5112         String sfx;
5113         parseName(fileName, path, basename, sfx);
5114         if (sfx == "cpp" || sfx == "c" || sfx == "cxx"   ||
5115             sfx == "cc" || sfx == "CC")
5116             {
5117             FileRec *fe         = new FileRec(FileRec::CFILE);
5118             fe->path            = path;
5119             fe->baseName        = basename;
5120             fe->suffix          = sfx;
5121             allFiles[fileName]  = fe;
5122             }
5123         else if (sfx == "h"   ||  sfx == "hh"  ||
5124                  sfx == "hpp" ||  sfx == "hxx")
5125             {
5126             FileRec *fe         = new FileRec(FileRec::HFILE);
5127             fe->path            = path;
5128             fe->baseName        = basename;
5129             fe->suffix          = sfx;
5130             allFiles[fileName]  = fe;
5131             }
5132         }
5134     if (!listDirectories(sourceDir, "", directories))
5135         return false;
5136         
5137     return true;
5144 /**
5145  * Get a character from the buffer at pos.  If out of range,
5146  * return -1 for safety
5147  */
5148 int DepTool::get(int pos)
5150     if (pos>depFileSize)
5151         return -1;
5152     return depFileBuf[pos];
5157 /**
5158  *  Skip over all whitespace characters beginning at pos.  Return
5159  *  the position of the first non-whitespace character.
5160  */
5161 int DepTool::skipwhite(int pos)
5163     while (pos < depFileSize)
5164         {
5165         int ch = get(pos);
5166         if (ch < 0)
5167             break;
5168         if (!isspace(ch))
5169             break;
5170         pos++;
5171         }
5172     return pos;
5176 /**
5177  *  Parse the buffer beginning at pos, for a word.  Fill
5178  *  'ret' with the result.  Return the position after the
5179  *  word.
5180  */
5181 int DepTool::getword(int pos, String &ret)
5183     while (pos < depFileSize)
5184         {
5185         int ch = get(pos);
5186         if (ch < 0)
5187             break;
5188         if (isspace(ch))
5189             break;
5190         ret.push_back((char)ch);
5191         pos++;
5192         }
5193     return pos;
5196 /**
5197  * Return whether the sequence of characters in the buffer
5198  * beginning at pos match the key,  for the length of the key
5199  */
5200 bool DepTool::sequ(int pos, char *key)
5202     while (*key)
5203         {
5204         if (*key != get(pos))
5205             return false;
5206         key++; pos++;
5207         }
5208     return true;
5213 /**
5214  *  Add an include file name to a file record.  If the name
5215  *  is not found in allFiles explicitly, try prepending include
5216  *  directory names to it and try again.
5217  */
5218 bool DepTool::addIncludeFile(FileRec *frec, const String &iname)
5221     std::map<String, FileRec *>::iterator iter =
5222            allFiles.find(iname);
5223     if (iter != allFiles.end()) //already exists
5224         {
5225          //h file in same dir
5226         FileRec *other = iter->second;
5227         //trace("local: '%s'", iname.c_str());
5228         frec->files[iname] = other;
5229         return true;
5230         }
5231     else 
5232         {
5233         //look in other dirs
5234         std::vector<String>::iterator diter;
5235         for (diter=directories.begin() ;
5236              diter!=directories.end() ; diter++)
5237             {
5238             String dfname = *diter;
5239             dfname.append("/");
5240             dfname.append(iname);
5241             iter = allFiles.find(dfname);
5242             if (iter != allFiles.end())
5243                 {
5244                 FileRec *other = iter->second;
5245                 //trace("other: '%s'", iname.c_str());
5246                 frec->files[dfname] = other;
5247                 return true;
5248                 }
5249             }
5250         }
5251     return true;
5256 /**
5257  *  Lightly parse a file to find the #include directives.  Do
5258  *  a bit of state machine stuff to make sure that the directive
5259  *  is valid.  (Like not in a comment).
5260  */
5261 bool DepTool::scanFile(const String &fname, FileRec *frec)
5263     String fileName;
5264     if (sourceDir.size() > 0)
5265         {
5266         fileName.append(sourceDir);
5267         fileName.append("/");
5268         }
5269     fileName.append(fname);
5270     String nativeName = getNativePath(fileName);
5271     FILE *f = fopen(nativeName.c_str(), "r");
5272     if (!f)
5273         {
5274         error("Could not open '%s' for reading", fname.c_str());
5275         return false;
5276         }
5277     String buf;
5278     while (!feof(f))
5279         {
5280         int len = fread(readBuf, 1, readBufSize, f);
5281         readBuf[len] = '\0';
5282         buf.append(readBuf);
5283         }
5284     fclose(f);
5286     depFileSize = buf.size();
5287     depFileBuf  = (char *)buf.c_str();
5288     int pos = 0;
5291     while (pos < depFileSize)
5292         {
5293         //trace("p:%c", get(pos));
5295         //# Block comment
5296         if (get(pos) == '/' && get(pos+1) == '*')
5297             {
5298             pos += 2;
5299             while (pos < depFileSize)
5300                 {
5301                 if (get(pos) == '*' && get(pos+1) == '/')
5302                     {
5303                     pos += 2;
5304                     break;
5305                     }
5306                 else
5307                     pos++;
5308                 }
5309             }
5310         //# Line comment
5311         else if (get(pos) == '/' && get(pos+1) == '/')
5312             {
5313             pos += 2;
5314             while (pos < depFileSize)
5315                 {
5316                 if (get(pos) == '\n')
5317                     {
5318                     pos++;
5319                     break;
5320                     }
5321                 else
5322                     pos++;
5323                 }
5324             }
5325         //# #include! yaay
5326         else if (sequ(pos, "#include"))
5327             {
5328             pos += 8;
5329             pos = skipwhite(pos);
5330             String iname;
5331             pos = getword(pos, iname);
5332             if (iname.size()>2)
5333                 {
5334                 iname = iname.substr(1, iname.size()-2);
5335                 addIncludeFile(frec, iname);
5336                 }
5337             }
5338         else
5339             {
5340             pos++;
5341             }
5342         }
5344     return true;
5349 /**
5350  *  Recursively check include lists to find all files in allFiles to which
5351  *  a given file is dependent.
5352  */
5353 bool DepTool::processDependency(FileRec *ofile,
5354                              FileRec *include,
5355                              int depth)
5357     std::map<String, FileRec *>::iterator iter;
5358     for (iter=include->files.begin() ; iter!=include->files.end() ; iter++)
5359         {
5360         String fname  = iter->first;
5361         if (ofile->files.find(fname) != ofile->files.end())
5362             {
5363             //trace("file '%s' already seen", fname.c_str());
5364             continue;
5365             }
5366         FileRec *child  = iter->second;
5367         ofile->files[fname] = child;
5368       
5369         processDependency(ofile, child, depth+1);
5370         }
5373     return true;
5380 /**
5381  *  Generate the file dependency list.
5382  */
5383 bool DepTool::generateDependencies()
5385     std::map<String, FileRec *>::iterator iter;
5386     //# First pass.  Scan for all includes
5387     for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5388         {
5389         FileRec *frec = iter->second;
5390         if (!scanFile(iter->first, frec))
5391             {
5392             //quit?
5393             }
5394         }
5396     //# Second pass.  Scan for all includes
5397     for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5398         {
5399         FileRec *include = iter->second;
5400         if (include->type == FileRec::CFILE)
5401             {
5402             String cFileName = iter->first;
5403             FileRec *ofile      = new FileRec(FileRec::OFILE);
5404             ofile->path         = include->path;
5405             ofile->baseName     = include->baseName;
5406             ofile->suffix       = include->suffix;
5407             String fname     = include->path;
5408             if (fname.size()>0)
5409                 fname.append("/");
5410             fname.append(include->baseName);
5411             fname.append(".o");
5412             depFiles[fname]    = ofile;
5413             //add the .c file first?   no, don't
5414             //ofile->files[cFileName] = include;
5415             
5416             //trace("ofile:%s", fname.c_str());
5418             processDependency(ofile, include, 0);
5419             }
5420         }
5422       
5423     return true;
5428 /**
5429  *  High-level call to generate deps and optionally save them
5430  */
5431 bool DepTool::generateDependencies(const String &fileName)
5433     if (!createFileList())
5434         return false;
5435     if (!generateDependencies())
5436         return false;
5437     if (!saveDepFile(fileName))
5438         return false;
5439     return true;
5443 /**
5444  *   This saves the dependency cache.
5445  */
5446 bool DepTool::saveDepFile(const String &fileName)
5448     time_t tim;
5449     time(&tim);
5451     FILE *f = fopen(fileName.c_str(), "w");
5452     if (!f)
5453         {
5454         trace("cannot open '%s' for writing", fileName.c_str());
5455         }
5456     fprintf(f, "<?xml version='1.0'?>\n");
5457     fprintf(f, "<!--\n");
5458     fprintf(f, "########################################################\n");
5459     fprintf(f, "## File: build.dep\n");
5460     fprintf(f, "## Generated by BuildTool at :%s", ctime(&tim));
5461     fprintf(f, "########################################################\n");
5462     fprintf(f, "-->\n");
5464     fprintf(f, "<dependencies source='%s'>\n\n", sourceDir.c_str());
5465     std::map<String, FileRec *>::iterator iter;
5466     for (iter=depFiles.begin() ; iter!=depFiles.end() ; iter++)
5467         {
5468         FileRec *frec = iter->second;
5469         if (frec->type == FileRec::OFILE)
5470             {
5471             fprintf(f, "<object path='%s' name='%s' suffix='%s'>\n",
5472                  frec->path.c_str(), frec->baseName.c_str(), frec->suffix.c_str());
5473             std::map<String, FileRec *>::iterator citer;
5474             for (citer=frec->files.begin() ; citer!=frec->files.end() ; citer++)
5475                 {
5476                 String cfname = citer->first;
5477                 fprintf(f, "    <dep name='%s'/>\n", cfname.c_str());
5478                 }
5479             fprintf(f, "</object>\n\n");
5480             }
5481         }
5483     fprintf(f, "</dependencies>\n");
5484     fprintf(f, "\n");
5485     fprintf(f, "<!--\n");
5486     fprintf(f, "########################################################\n");
5487     fprintf(f, "## E N D\n");
5488     fprintf(f, "########################################################\n");
5489     fprintf(f, "-->\n");
5491     fclose(f);
5493     return true;
5499 /**
5500  *   This loads the dependency cache.
5501  */
5502 std::vector<DepRec> DepTool::loadDepFile(const String &depFile)
5504     std::vector<DepRec> result;
5505     
5506     Parser parser;
5507     Element *root = parser.parseFile(depFile.c_str());
5508     if (!root)
5509         {
5510         //error("Could not open %s for reading", depFile.c_str());
5511         return result;
5512         }
5514     if (root->getChildren().size()==0 ||
5515         root->getChildren()[0]->getName()!="dependencies")
5516         {
5517         error("Main xml element should be <dependencies>");
5518         delete root;
5519         return result;
5520         }
5522     //########## Start parsing
5523     Element *depList = root->getChildren()[0];
5525     std::vector<Element *> objects = depList->getChildren();
5526     for (unsigned int i=0 ; i<objects.size() ; i++)
5527         {
5528         Element *objectElem = objects[i];
5529         String tagName = objectElem->getName();
5530         if (tagName == "object")
5531             {
5532             String objName   = objectElem->getAttribute("name");
5533              //trace("object:%s", objName.c_str());
5534             DepRec depObject(objName);
5535             depObject.path   = objectElem->getAttribute("path");
5536             depObject.suffix = objectElem->getAttribute("suffix");
5537             //########## DESCRIPTION
5538             std::vector<Element *> depElems = objectElem->getChildren();
5539             for (unsigned int i=0 ; i<depElems.size() ; i++)
5540                 {
5541                 Element *depElem = depElems[i];
5542                 tagName = depElem->getName();
5543                 if (tagName == "dep")
5544                     {
5545                     String depName = depElem->getAttribute("name");
5546                     //trace("    dep:%s", depName.c_str());
5547                     depObject.files.push_back(depName);
5548                     }
5549                 }
5550             //Insert into the result list, in a sorted manner
5551             bool inserted = false;
5552             std::vector<DepRec>::iterator iter;
5553             for (iter = result.begin() ; iter != result.end() ; iter++)
5554                 {
5555                 String vpath = iter->path;
5556                 vpath.append("/");
5557                 vpath.append(iter->name);
5558                 String opath = depObject.path;
5559                 opath.append("/");
5560                 opath.append(depObject.name);
5561                 if (vpath > opath)
5562                     {
5563                     inserted = true;
5564                     iter = result.insert(iter, depObject);
5565                     break;
5566                     }
5567                 }
5568             if (!inserted)
5569                 result.push_back(depObject);
5570             }
5571         }
5573     delete root;
5575     return result;
5579 /**
5580  *   This loads the dependency cache.
5581  */
5582 std::vector<DepRec> DepTool::getDepFile(const String &depFile,
5583                    bool forceRefresh)
5585     std::vector<DepRec> result;
5586     if (forceRefresh)
5587         {
5588         generateDependencies(depFile);
5589         result = loadDepFile(depFile);
5590         }
5591     else
5592         {
5593         //try once
5594         result = loadDepFile(depFile);
5595         if (result.size() == 0)
5596             {
5597             //fail? try again
5598             generateDependencies(depFile);
5599             result = loadDepFile(depFile);
5600             }
5601         }
5602     return result;
5608 //########################################################################
5609 //# T A S K
5610 //########################################################################
5611 //forward decl
5612 class Target;
5613 class Make;
5615 /**
5616  *
5617  */
5618 class Task : public MakeBase
5621 public:
5623     typedef enum
5624         {
5625         TASK_NONE,
5626         TASK_CC,
5627         TASK_COPY,
5628         TASK_DELETE,
5629         TASK_JAR,
5630         TASK_JAVAC,
5631         TASK_LINK,
5632         TASK_MAKEFILE,
5633         TASK_MKDIR,
5634         TASK_MSGFMT,
5635         TASK_RANLIB,
5636         TASK_RC,
5637         TASK_SHAREDLIB,
5638         TASK_STATICLIB,
5639         TASK_STRIP,
5640         TASK_TSTAMP
5641         } TaskType;
5642         
5644     /**
5645      *
5646      */
5647     Task(MakeBase &par) : parent(par)
5648         { init(); }
5650     /**
5651      *
5652      */
5653     Task(const Task &other) : parent(other.parent)
5654         { init(); assign(other); }
5656     /**
5657      *
5658      */
5659     Task &operator=(const Task &other)
5660         { assign(other); return *this; }
5662     /**
5663      *
5664      */
5665     virtual ~Task()
5666         { }
5669     /**
5670      *
5671      */
5672     virtual MakeBase &getParent()
5673         { return parent; }
5675      /**
5676      *
5677      */
5678     virtual int  getType()
5679         { return type; }
5681     /**
5682      *
5683      */
5684     virtual void setType(int val)
5685         { type = val; }
5687     /**
5688      *
5689      */
5690     virtual String getName()
5691         { return name; }
5693     /**
5694      *
5695      */
5696     virtual bool execute()
5697         { return true; }
5699     /**
5700      *
5701      */
5702     virtual bool parse(Element *elem)
5703         { return true; }
5705     /**
5706      *
5707      */
5708     Task *createTask(Element *elem);
5711 protected:
5713     void init()
5714         {
5715         type = TASK_NONE;
5716         name = "none";
5717         }
5719     void assign(const Task &other)
5720         {
5721         type = other.type;
5722         name = other.name;
5723         }
5724         
5725     String getAttribute(Element *elem, const String &attrName)
5726         {
5727         String str;
5728         return str;
5729         }
5731     MakeBase &parent;
5733     int type;
5735     String name;
5736 };
5740 /**
5741  * This task runs the C/C++ compiler.  The compiler is invoked
5742  * for all .c or .cpp files which are newer than their correcsponding
5743  * .o files.  
5744  */
5745 class TaskCC : public Task
5747 public:
5749     TaskCC(MakeBase &par) : Task(par)
5750         {
5751         type = TASK_CC; name = "cc";
5752         ccCommand   = "gcc";
5753         cxxCommand  = "g++";
5754         source      = ".";
5755         dest        = ".";
5756         flags       = "";
5757         defines     = "";
5758         includes    = "";
5759         fileSet.clear();
5760         }
5762     virtual ~TaskCC()
5763         {}
5765     virtual bool needsCompiling(const DepRec &depRec,
5766               const String &src, const String &dest)
5767         {
5768         return false;
5769         }
5771     virtual bool execute()
5772         {
5773         if (!listFiles(parent, fileSet))
5774             return false;
5776         bool refreshCache = false;
5777         String fullName = parent.resolve("build.dep");
5778         if (isNewerThan(parent.getURI().getPath(), fullName))
5779             {
5780             status("          : regenerating C/C++ dependency cache");
5781             refreshCache = true;
5782             }
5784         DepTool depTool;
5785         depTool.setSourceDirectory(source);
5786         depTool.setFileList(fileSet.getFiles());
5787         std::vector<DepRec> deps =
5788              depTool.getDepFile("build.dep", refreshCache);
5789         
5790         String incs;
5791         incs.append("-I");
5792         incs.append(parent.resolve("."));
5793         incs.append(" ");
5794         if (includes.size()>0)
5795             {
5796             incs.append(includes);
5797             incs.append(" ");
5798             }
5799         std::set<String> paths;
5800         std::vector<DepRec>::iterator viter;
5801         for (viter=deps.begin() ; viter!=deps.end() ; viter++)
5802             {
5803             DepRec dep = *viter;
5804             if (dep.path.size()>0)
5805                 paths.insert(dep.path);
5806             }
5807         if (source.size()>0)
5808             {
5809             incs.append(" -I");
5810             incs.append(parent.resolve(source));
5811             incs.append(" ");
5812             }
5813         std::set<String>::iterator setIter;
5814         for (setIter=paths.begin() ; setIter!=paths.end() ; setIter++)
5815             {
5816             incs.append(" -I");
5817             String dname;
5818             if (source.size()>0)
5819                 {
5820                 dname.append(source);
5821                 dname.append("/");
5822                 }
5823             dname.append(*setIter);
5824             incs.append(parent.resolve(dname));
5825             }
5826         std::vector<String> cfiles;
5827         for (viter=deps.begin() ; viter!=deps.end() ; viter++)
5828             {
5829             DepRec dep = *viter;
5831             //## Select command
5832             String sfx = dep.suffix;
5833             String command = ccCommand;
5834             if (sfx == "cpp" || sfx == "c++" || sfx == "cc"
5835                  || sfx == "CC")
5836                 command = cxxCommand;
5837  
5838             //## Make paths
5839             String destPath = dest;
5840             String srcPath  = source;
5841             if (dep.path.size()>0)
5842                 {
5843                 destPath.append("/");
5844                 destPath.append(dep.path);
5845                 srcPath.append("/");
5846                 srcPath.append(dep.path);
5847                 }
5848             //## Make sure destination directory exists
5849             if (!createDirectory(destPath))
5850                 return false;
5851                 
5852             //## Check whether it needs to be done
5853             String destName;
5854             if (destPath.size()>0)
5855                 {
5856                 destName.append(destPath);
5857                 destName.append("/");
5858                 }
5859             destName.append(dep.name);
5860             destName.append(".o");
5861             String destFullName = parent.resolve(destName);
5862             String srcName;
5863             if (srcPath.size()>0)
5864                 {
5865                 srcName.append(srcPath);
5866                 srcName.append("/");
5867                 }
5868             srcName.append(dep.name);
5869             srcName.append(".");
5870             srcName.append(dep.suffix);
5871             String srcFullName = parent.resolve(srcName);
5872             bool compileMe = false;
5873             if (isNewerThan(srcFullName, destFullName))
5874                 {
5875                 status("          : compile of %s required by %s",
5876                         destFullName.c_str(), srcFullName.c_str());
5877                 compileMe = true;
5878                 }
5879             else
5880                 {
5881                 for (unsigned int i=0 ; i<dep.files.size() ; i++)
5882                     {
5883                     String depName;
5884                     if (srcPath.size()>0)
5885                         {
5886                         depName.append(srcPath);
5887                         depName.append("/");
5888                         }
5889                     depName.append(dep.files[i]);
5890                     String depFullName = parent.resolve(depName);
5891                     if (isNewerThan(depFullName, destFullName))
5892                         {
5893                         status("          : compile of %s required by %s",
5894                                 destFullName.c_str(), depFullName.c_str());
5895                         compileMe = true;
5896                         break;
5897                         }
5898                     }
5899                 }
5900             if (!compileMe)
5901                 {
5902                 continue;
5903                 }
5905             //## Assemble the command
5906             String cmd = command;
5907             cmd.append(" -c ");
5908             cmd.append(flags);
5909             cmd.append(" ");
5910             cmd.append(defines);
5911             cmd.append(" ");
5912             cmd.append(incs);
5913             cmd.append(" ");
5914             cmd.append(srcFullName);
5915             cmd.append(" -o ");
5916             cmd.append(destFullName);
5918             //## Execute the command
5920             String outString, errString;
5921             if (!executeCommand(cmd.c_str(), "", outString, errString))
5922                 {
5923                 error("problem compiling: %s", errString.c_str());
5924                 return false;
5925                 }
5926             }
5927         
5928         return true;
5929         }
5931     virtual bool parse(Element *elem)
5932         {
5933         String s;
5934         if (!parent.getAttribute(elem, "command", s))
5935             return false;
5936         if (s.size()>0) { ccCommand = s; cxxCommand = s; }
5937         if (!parent.getAttribute(elem, "cc", s))
5938             return false;
5939         if (s.size()>0) ccCommand = s;
5940         if (!parent.getAttribute(elem, "cxx", s))
5941             return false;
5942         if (s.size()>0) cxxCommand = s;
5943         if (!parent.getAttribute(elem, "destdir", s))
5944             return false;
5945         if (s.size()>0) dest = s;
5947         std::vector<Element *> children = elem->getChildren();
5948         for (unsigned int i=0 ; i<children.size() ; i++)
5949             {
5950             Element *child = children[i];
5951             String tagName = child->getName();
5952             if (tagName == "flags")
5953                 {
5954                 if (!parent.getValue(child, flags))
5955                     return false;
5956                 flags = strip(flags);
5957                 }
5958             else if (tagName == "includes")
5959                 {
5960                 if (!parent.getValue(child, includes))
5961                     return false;
5962                 includes = strip(includes);
5963                 }
5964             else if (tagName == "defines")
5965                 {
5966                 if (!parent.getValue(child, defines))
5967                     return false;
5968                 defines = strip(defines);
5969                 }
5970             else if (tagName == "fileset")
5971                 {
5972                 if (!parseFileSet(child, parent, fileSet))
5973                     return false;
5974                 source = fileSet.getDirectory();
5975                 }
5976             }
5978         return true;
5979         }
5980         
5981 protected:
5983     String ccCommand;
5984     String cxxCommand;
5985     String source;
5986     String dest;
5987     String flags;
5988     String defines;
5989     String includes;
5990     FileSet fileSet;
5991     
5992 };
5996 /**
5997  *
5998  */
5999 class TaskCopy : public Task
6001 public:
6003     typedef enum
6004         {
6005         CP_NONE,
6006         CP_TOFILE,
6007         CP_TODIR
6008         } CopyType;
6010     TaskCopy(MakeBase &par) : Task(par)
6011         {
6012         type = TASK_COPY; name = "copy";
6013         cptype = CP_NONE;
6014         verbose = false;
6015         haveFileSet = false;
6016         }
6018     virtual ~TaskCopy()
6019         {}
6021     virtual bool execute()
6022         {
6023         switch (cptype)
6024            {
6025            case CP_TOFILE:
6026                {
6027                if (fileName.size()>0)
6028                    {
6029                    status("          : %s to %s",
6030                         fileName.c_str(), toFileName.c_str());
6031                    String fullSource = parent.resolve(fileName);
6032                    String fullDest = parent.resolve(toFileName);
6033                    //trace("copy %s to file %s", fullSource.c_str(),
6034                    //                       fullDest.c_str());
6035                    if (!isRegularFile(fullSource))
6036                        {
6037                        error("copy : file %s does not exist", fullSource.c_str());
6038                        return false;
6039                        }
6040                    if (!isNewerThan(fullSource, fullDest))
6041                        {
6042                        return true;
6043                        }
6044                    if (!copyFile(fullSource, fullDest))
6045                        return false;
6046                    status("          : 1 file copied");
6047                    }
6048                return true;
6049                }
6050            case CP_TODIR:
6051                {
6052                if (haveFileSet)
6053                    {
6054                    if (!listFiles(parent, fileSet))
6055                        return false;
6056                    String fileSetDir = fileSet.getDirectory();
6058                    status("          : %s to %s",
6059                        fileSetDir.c_str(), toDirName.c_str());
6061                    int nrFiles = 0;
6062                    for (unsigned int i=0 ; i<fileSet.size() ; i++)
6063                        {
6064                        String fileName = fileSet[i];
6066                        String sourcePath;
6067                        if (fileSetDir.size()>0)
6068                            {
6069                            sourcePath.append(fileSetDir);
6070                            sourcePath.append("/");
6071                            }
6072                        sourcePath.append(fileName);
6073                        String fullSource = parent.resolve(sourcePath);
6074                        
6075                        //Get the immediate parent directory's base name
6076                        String baseFileSetDir = fileSetDir;
6077                        unsigned int pos = baseFileSetDir.find_last_of('/');
6078                        if (pos!=baseFileSetDir.npos &&
6079                                   pos < baseFileSetDir.size()-1)
6080                            baseFileSetDir =
6081                               baseFileSetDir.substr(pos+1,
6082                                    baseFileSetDir.size());
6083                        //Now make the new path
6084                        String destPath;
6085                        if (toDirName.size()>0)
6086                            {
6087                            destPath.append(toDirName);
6088                            destPath.append("/");
6089                            }
6090                        if (baseFileSetDir.size()>0)
6091                            {
6092                            destPath.append(baseFileSetDir);
6093                            destPath.append("/");
6094                            }
6095                        destPath.append(fileName);
6096                        String fullDest = parent.resolve(destPath);
6097                        //trace("fileName:%s", fileName.c_str());
6098                        //trace("copy %s to new dir : %s", fullSource.c_str(),
6099                        //                   fullDest.c_str());
6100                        if (!isNewerThan(fullSource, fullDest))
6101                            {
6102                            //trace("copy skipping %s", fullSource.c_str());
6103                            continue;
6104                            }
6105                        if (!copyFile(fullSource, fullDest))
6106                            return false;
6107                        nrFiles++;
6108                        }
6109                    status("          : %d file(s) copied", nrFiles);
6110                    }
6111                else //file source
6112                    {
6113                    //For file->dir we want only the basename of
6114                    //the source appended to the dest dir
6115                    status("          : %s to %s", 
6116                        fileName.c_str(), toDirName.c_str());
6117                    String baseName = fileName;
6118                    unsigned int pos = baseName.find_last_of('/');
6119                    if (pos!=baseName.npos && pos<baseName.size()-1)
6120                        baseName = baseName.substr(pos+1, baseName.size());
6121                    String fullSource = parent.resolve(fileName);
6122                    String destPath;
6123                    if (toDirName.size()>0)
6124                        {
6125                        destPath.append(toDirName);
6126                        destPath.append("/");
6127                        }
6128                    destPath.append(baseName);
6129                    String fullDest = parent.resolve(destPath);
6130                    //trace("copy %s to new dir : %s", fullSource.c_str(),
6131                    //                       fullDest.c_str());
6132                    if (!isRegularFile(fullSource))
6133                        {
6134                        error("copy : file %s does not exist", fullSource.c_str());
6135                        return false;
6136                        }
6137                    if (!isNewerThan(fullSource, fullDest))
6138                        {
6139                        return true;
6140                        }
6141                    if (!copyFile(fullSource, fullDest))
6142                        return false;
6143                    status("          : 1 file copied");
6144                    }
6145                return true;
6146                }
6147            }
6148         return true;
6149         }
6152     virtual bool parse(Element *elem)
6153         {
6154         if (!parent.getAttribute(elem, "file", fileName))
6155             return false;
6156         if (!parent.getAttribute(elem, "tofile", toFileName))
6157             return false;
6158         if (toFileName.size() > 0)
6159             cptype = CP_TOFILE;
6160         if (!parent.getAttribute(elem, "todir", toDirName))
6161             return false;
6162         if (toDirName.size() > 0)
6163             cptype = CP_TODIR;
6164         String ret;
6165         if (!parent.getAttribute(elem, "verbose", ret))
6166             return false;
6167         if (ret.size()>0 && !getBool(ret, verbose))
6168             return false;
6169             
6170         haveFileSet = false;
6171         
6172         std::vector<Element *> children = elem->getChildren();
6173         for (unsigned int i=0 ; i<children.size() ; i++)
6174             {
6175             Element *child = children[i];
6176             String tagName = child->getName();
6177             if (tagName == "fileset")
6178                 {
6179                 if (!parseFileSet(child, parent, fileSet))
6180                     {
6181                     error("problem getting fileset");
6182                     return false;
6183                     }
6184                 haveFileSet = true;
6185                 }
6186             }
6188         //Perform validity checks
6189         if (fileName.size()>0 && fileSet.size()>0)
6190             {
6191             error("<copy> can only have one of : file= and <fileset>");
6192             return false;
6193             }
6194         if (toFileName.size()>0 && toDirName.size()>0)
6195             {
6196             error("<copy> can only have one of : tofile= or todir=");
6197             return false;
6198             }
6199         if (haveFileSet && toDirName.size()==0)
6200             {
6201             error("a <copy> task with a <fileset> must have : todir=");
6202             return false;
6203             }
6204         if (cptype == CP_TOFILE && fileName.size()==0)
6205             {
6206             error("<copy> tofile= must be associated with : file=");
6207             return false;
6208             }
6209         if (cptype == CP_TODIR && fileName.size()==0 && !haveFileSet)
6210             {
6211             error("<copy> todir= must be associated with : file= or <fileset>");
6212             return false;
6213             }
6215         return true;
6216         }
6217         
6218 private:
6220     int cptype;
6221     String fileName;
6222     FileSet fileSet;
6223     String toFileName;
6224     String toDirName;
6225     bool verbose;
6226     bool haveFileSet;
6227 };
6230 /**
6231  *
6232  */
6233 class TaskDelete : public Task
6235 public:
6237     typedef enum
6238         {
6239         DEL_FILE,
6240         DEL_DIR,
6241         DEL_FILESET
6242         } DeleteType;
6244     TaskDelete(MakeBase &par) : Task(par)
6245         { 
6246           type        = TASK_DELETE;
6247           name        = "delete";
6248           delType     = DEL_FILE;
6249           verbose     = false;
6250           quiet       = false;
6251           failOnError = true;
6252         }
6254     virtual ~TaskDelete()
6255         {}
6257     virtual bool execute()
6258         {
6259         struct stat finfo;
6260         switch (delType)
6261             {
6262             case DEL_FILE:
6263                 {
6264                 status("          : %s", fileName.c_str());
6265                 String fullName = parent.resolve(fileName);
6266                 char *fname = (char *)fullName.c_str();
6267                 //does not exist
6268                 if (stat(fname, &finfo)<0)
6269                     return true;
6270                 //exists but is not a regular file
6271                 if (!S_ISREG(finfo.st_mode))
6272                     {
6273                     error("<delete> failed. '%s' exists and is not a regular file",
6274                           fname);
6275                     return false;
6276                     }
6277                 if (remove(fname)<0)
6278                     {
6279                     error("<delete> failed: %s", strerror(errno));
6280                     return false;
6281                     }
6282                 return true;
6283                 }
6284             case DEL_DIR:
6285                 {
6286                 status("          : %s", dirName.c_str());
6287                 String fullDir = parent.resolve(dirName);
6288                 if (!removeDirectory(fullDir))
6289                     return false;
6290                 return true;
6291                 }
6292             }
6293         return true;
6294         }
6296     virtual bool parse(Element *elem)
6297         {
6298         if (!parent.getAttribute(elem, "file", fileName))
6299             return false;
6300         if (fileName.size() > 0)
6301             delType = DEL_FILE;
6302         if (!parent.getAttribute(elem, "dir", dirName))
6303             return false;
6304         if (dirName.size() > 0)
6305             delType = DEL_DIR;
6306         if (fileName.size()>0 && dirName.size()>0)
6307             {
6308             error("<delete> can only have one attribute of file= or dir=");
6309             return false;
6310             }
6311         String ret;
6312         if (!parent.getAttribute(elem, "verbose", ret))
6313             return false;
6314         if (ret.size()>0 && !getBool(ret, verbose))
6315             return false;
6316         if (!parent.getAttribute(elem, "quiet", ret))
6317             return false;
6318         if (ret.size()>0 && !getBool(ret, quiet))
6319             return false;
6320         if (!parent.getAttribute(elem, "failonerror", ret))
6321             return false;
6322         if (ret.size()>0 && !getBool(ret, failOnError))
6323             return false;
6324         return true;
6325         }
6327 private:
6329     int delType;
6330     String dirName;
6331     String fileName;
6332     bool verbose;
6333     bool quiet;
6334     bool failOnError;
6335 };
6338 /**
6339  *
6340  */
6341 class TaskJar : public Task
6343 public:
6345     TaskJar(MakeBase &par) : Task(par)
6346         { type = TASK_JAR; name = "jar"; }
6348     virtual ~TaskJar()
6349         {}
6351     virtual bool execute()
6352         {
6353         return true;
6354         }
6356     virtual bool parse(Element *elem)
6357         {
6358         return true;
6359         }
6360 };
6363 /**
6364  *
6365  */
6366 class TaskJavac : public Task
6368 public:
6370     TaskJavac(MakeBase &par) : Task(par)
6371         { type = TASK_JAVAC; name = "javac"; }
6373     virtual ~TaskJavac()
6374         {}
6376     virtual bool execute()
6377         {
6378         return true;
6379         }
6381     virtual bool parse(Element *elem)
6382         {
6383         return true;
6384         }
6385 };
6388 /**
6389  *
6390  */
6391 class TaskLink : public Task
6393 public:
6395     TaskLink(MakeBase &par) : Task(par)
6396         {
6397         type = TASK_LINK; name = "link";
6398         command = "g++";
6399         doStrip = false;
6400                 stripCommand = "strip";
6401                 objcopyCommand = "objcopy";
6402         }
6404     virtual ~TaskLink()
6405         {}
6407     virtual bool execute()
6408         {
6409         if (!listFiles(parent, fileSet))
6410             return false;
6411         String fileSetDir = fileSet.getDirectory();
6412         //trace("%d files in %s", fileSet.size(), fileSetDir.c_str());
6413         bool doit = false;
6414         String fullTarget = parent.resolve(fileName);
6415         String cmd = command;
6416         cmd.append(" -o ");
6417         cmd.append(fullTarget);
6418         cmd.append(" ");
6419         cmd.append(flags);
6420         for (unsigned int i=0 ; i<fileSet.size() ; i++)
6421             {
6422             cmd.append(" ");
6423             String obj;
6424             if (fileSetDir.size()>0)
6425                 {
6426                 obj.append(fileSetDir);
6427                 obj.append("/");
6428                 }
6429             obj.append(fileSet[i]);
6430             String fullObj = parent.resolve(obj);
6431             cmd.append(fullObj);
6432             //trace("link: tgt:%s obj:%s", fullTarget.c_str(),
6433             //          fullObj.c_str());
6434             if (isNewerThan(fullObj, fullTarget))
6435                 doit = true;
6436             }
6437         cmd.append(" ");
6438         cmd.append(libs);
6439         if (!doit)
6440             {
6441             //trace("link not needed");
6442             return true;
6443             }
6444         //trace("LINK cmd:%s", cmd.c_str());
6447         String outbuf, errbuf;
6448         if (!executeCommand(cmd.c_str(), "", outbuf, errbuf))
6449             {
6450             error("LINK problem: %s", errbuf.c_str());
6451             return false;
6452             }
6454         if (symFileName.size()>0)
6455             {
6456             String symFullName = parent.resolve(symFileName);
6457             cmd = objcopyCommand;
6458             cmd.append(" --only-keep-debug ");
6459             cmd.append(getNativePath(fullTarget));
6460             cmd.append(" ");
6461             cmd.append(getNativePath(symFullName));
6462             if (!executeCommand(cmd, "", outbuf, errbuf))
6463                 {
6464                 error("<strip> symbol file failed : %s", errbuf.c_str());
6465                 return false;
6466                 }
6467             }
6468             
6469         if (doStrip)
6470             {
6471             cmd = stripCommand;
6472             cmd.append(" ");
6473             cmd.append(getNativePath(fullTarget));
6474             if (!executeCommand(cmd, "", outbuf, errbuf))
6475                {
6476                error("<strip> failed : %s", errbuf.c_str());
6477                return false;
6478                }
6479             }
6481         return true;
6482         }
6484     virtual bool parse(Element *elem)
6485         {
6486         String s;
6487         if (!parent.getAttribute(elem, "command", s))
6488             return false;
6489         if (s.size()>0)
6490             command = s;
6491         if (!parent.getAttribute(elem, "objcopycommand", s))
6492             return false;
6493         if (s.size()>0)
6494             objcopyCommand = s;
6495         if (!parent.getAttribute(elem, "stripcommand", s))
6496             return false;
6497         if (s.size()>0)
6498             stripCommand = s;
6499         if (!parent.getAttribute(elem, "out", fileName))
6500             return false;
6501         if (!parent.getAttribute(elem, "strip", s))
6502             return false;
6503         if (!getBool(s, doStrip))
6504             return false;
6505         if (!parent.getAttribute(elem, "symfile", symFileName))
6506             return false;
6507             
6508         std::vector<Element *> children = elem->getChildren();
6509         for (unsigned int i=0 ; i<children.size() ; i++)
6510             {
6511             Element *child = children[i];
6512             String tagName = child->getName();
6513             if (tagName == "fileset")
6514                 {
6515                 if (!parseFileSet(child, parent, fileSet))
6516                     return false;
6517                 }
6518             else if (tagName == "flags")
6519                 {
6520                 if (!parent.getValue(child, flags))
6521                     return false;
6522                 flags = strip(flags);
6523                 }
6524             else if (tagName == "libs")
6525                 {
6526                 if (!parent.getValue(child, libs))
6527                     return false;
6528                 libs = strip(libs);
6529                 }
6530             }
6531         return true;
6532         }
6534 private:
6536     String  command;
6537     String  fileName;
6538     String  flags;
6539     String  libs;
6540     FileSet fileSet;
6541     bool    doStrip;
6542     String  symFileName;
6543     String  stripCommand;
6544     String  objcopyCommand;
6546 };
6550 /**
6551  * Create a named directory
6552  */
6553 class TaskMakeFile : public Task
6555 public:
6557     TaskMakeFile(MakeBase &par) : Task(par)
6558         { type = TASK_MAKEFILE; name = "makefile"; }
6560     virtual ~TaskMakeFile()
6561         {}
6563     virtual bool execute()
6564         {
6565         status("          : %s", fileName.c_str());
6566         String fullName = parent.resolve(fileName);
6567         if (!isNewerThan(parent.getURI().getPath(), fullName))
6568             {
6569             //trace("skipped <makefile>");
6570             return true;
6571             }
6572         //trace("fullName:%s", fullName.c_str());
6573         FILE *f = fopen(fullName.c_str(), "w");
6574         if (!f)
6575             {
6576             error("<makefile> could not open %s for writing : %s",
6577                 fullName.c_str(), strerror(errno));
6578             return false;
6579             }
6580         for (unsigned int i=0 ; i<text.size() ; i++)
6581             fputc(text[i], f);
6582         fputc('\n', f);
6583         fclose(f);
6584         return true;
6585         }
6587     virtual bool parse(Element *elem)
6588         {
6589         if (!parent.getAttribute(elem, "file", fileName))
6590             return false;
6591         if (fileName.size() == 0)
6592             {
6593             error("<makefile> requires 'file=\"filename\"' attribute");
6594             return false;
6595             }
6596         if (!parent.getValue(elem, text))
6597             return false;
6598         text = leftJustify(text);
6599         //trace("dirname:%s", dirName.c_str());
6600         return true;
6601         }
6603 private:
6605     String fileName;
6606     String text;
6607 };
6611 /**
6612  * Create a named directory
6613  */
6614 class TaskMkDir : public Task
6616 public:
6618     TaskMkDir(MakeBase &par) : Task(par)
6619         { type = TASK_MKDIR; name = "mkdir"; }
6621     virtual ~TaskMkDir()
6622         {}
6624     virtual bool execute()
6625         {
6626         status("          : %s", dirName.c_str());
6627         String fullDir = parent.resolve(dirName);
6628         //trace("fullDir:%s", fullDir.c_str());
6629         if (!createDirectory(fullDir))
6630             return false;
6631         return true;
6632         }
6634     virtual bool parse(Element *elem)
6635         {
6636         if (!parent.getAttribute(elem, "dir", dirName))
6637             return false;
6638         if (dirName.size() == 0)
6639             {
6640             error("<mkdir> requires 'dir=\"dirname\"' attribute");
6641             return false;
6642             }
6643         return true;
6644         }
6646 private:
6648     String dirName;
6649 };
6653 /**
6654  * Create a named directory
6655  */
6656 class TaskMsgFmt: public Task
6658 public:
6660     TaskMsgFmt(MakeBase &par) : Task(par)
6661          {
6662          type    = TASK_MSGFMT;
6663          name    = "msgfmt";
6664          command = "msgfmt";
6665          owndir  = false;
6666          outName = "";
6667          }
6669     virtual ~TaskMsgFmt()
6670         {}
6672     virtual bool execute()
6673         {
6674         if (!listFiles(parent, fileSet))
6675             return false;
6676         String fileSetDir = fileSet.getDirectory();
6678         //trace("msgfmt: %d", fileSet.size());
6679         for (unsigned int i=0 ; i<fileSet.size() ; i++)
6680             {
6681             String fileName = fileSet[i];
6682             if (getSuffix(fileName) != "po")
6683                 continue;
6684             String sourcePath;
6685             if (fileSetDir.size()>0)
6686                 {
6687                 sourcePath.append(fileSetDir);
6688                 sourcePath.append("/");
6689                 }
6690             sourcePath.append(fileName);
6691             String fullSource = parent.resolve(sourcePath);
6693             String destPath;
6694             if (toDirName.size()>0)
6695                 {
6696                 destPath.append(toDirName);
6697                 destPath.append("/");
6698                 }
6699             if (owndir)
6700                 {
6701                 String subdir = fileName;
6702                 unsigned int pos = subdir.find_last_of('.');
6703                 if (pos != subdir.npos)
6704                     subdir = subdir.substr(0, pos);
6705                 destPath.append(subdir);
6706                 destPath.append("/");
6707                 }
6708             //Pick the output file name
6709             if (outName.size() > 0)
6710                 {
6711                 destPath.append(outName);
6712                 }
6713             else
6714                 {
6715                 destPath.append(fileName);
6716                 destPath[destPath.size()-2] = 'm';
6717                 }
6719             String fullDest = parent.resolve(destPath);
6721             if (!isNewerThan(fullSource, fullDest))
6722                 {
6723                 //trace("skip %s", fullSource.c_str());
6724                 continue;
6725                 }
6726                 
6727             String cmd = command;
6728             cmd.append(" ");
6729             cmd.append(fullSource);
6730             cmd.append(" -o ");
6731             cmd.append(fullDest);
6732             
6733             int pos = fullDest.find_last_of('/');
6734             if (pos>0)
6735                 {
6736                 String fullDestPath = fullDest.substr(0, pos);
6737                 if (!createDirectory(fullDestPath))
6738                     return false;
6739                 }
6743             String outString, errString;
6744             if (!executeCommand(cmd.c_str(), "", outString, errString))
6745                 {
6746                 error("<msgfmt> problem: %s", errString.c_str());
6747                 return false;
6748                 }
6749             }
6751         return true;
6752         }
6754     virtual bool parse(Element *elem)
6755         {
6756         String s;
6757         if (!parent.getAttribute(elem, "command", s))
6758             return false;
6759         if (s.size()>0)
6760             command = s;
6761         if (!parent.getAttribute(elem, "todir", toDirName))
6762             return false;
6763         if (!parent.getAttribute(elem, "out", outName))
6764             return false;
6765         if (!parent.getAttribute(elem, "owndir", s))
6766             return false;
6767         if (!getBool(s, owndir))
6768             return false;
6769             
6770         std::vector<Element *> children = elem->getChildren();
6771         for (unsigned int i=0 ; i<children.size() ; i++)
6772             {
6773             Element *child = children[i];
6774             String tagName = child->getName();
6775             if (tagName == "fileset")
6776                 {
6777                 if (!parseFileSet(child, parent, fileSet))
6778                     return false;
6779                 }
6780             }
6781         return true;
6782         }
6784 private:
6786     String  command;
6787     String  toDirName;
6788     String  outName;
6789     FileSet fileSet;
6790     bool    owndir;
6792 };
6798 /**
6799  *  Process an archive to allow random access
6800  */
6801 class TaskRanlib : public Task
6803 public:
6805     TaskRanlib(MakeBase &par) : Task(par)
6806         {
6807         type = TASK_RANLIB; name = "ranlib";
6808         command = "ranlib";
6809         }
6811     virtual ~TaskRanlib()
6812         {}
6814     virtual bool execute()
6815         {
6816         String fullName = parent.resolve(fileName);
6817         //trace("fullDir:%s", fullDir.c_str());
6818         String cmd = command;
6819         cmd.append(" ");
6820         cmd.append(fullName);
6821         String outbuf, errbuf;
6822         if (!executeCommand(cmd, "", outbuf, errbuf))
6823             return false;
6824         return true;
6825         }
6827     virtual bool parse(Element *elem)
6828         {
6829         String s;
6830         if (!parent.getAttribute(elem, "command", s))
6831             return false;
6832         if (s.size()>0)
6833            command = s;
6834         if (!parent.getAttribute(elem, "file", fileName))
6835             return false;
6836         if (fileName.size() == 0)
6837             {
6838             error("<ranlib> requires 'file=\"fileNname\"' attribute");
6839             return false;
6840             }
6841         return true;
6842         }
6844 private:
6846     String fileName;
6847     String command;
6848 };
6852 /**
6853  * Run the "ar" command to archive .o's into a .a
6854  */
6855 class TaskRC : public Task
6857 public:
6859     TaskRC(MakeBase &par) : Task(par)
6860         {
6861         type = TASK_RC; name = "rc";
6862         command = "windres";
6863         }
6865     virtual ~TaskRC()
6866         {}
6868     virtual bool execute()
6869         {
6870         String fullFile = parent.resolve(fileName);
6871         String fullOut  = parent.resolve(outName);
6872         if (!isNewerThan(fullFile, fullOut))
6873             return true;
6874         String cmd = command;
6875         cmd.append(" -o ");
6876         cmd.append(fullOut);
6877         cmd.append(" ");
6878         cmd.append(flags);
6879         cmd.append(" ");
6880         cmd.append(fullFile);
6882         String outString, errString;
6883         if (!executeCommand(cmd.c_str(), "", outString, errString))
6884             {
6885             error("RC problem: %s", errString.c_str());
6886             return false;
6887             }
6888         return true;
6889         }
6891     virtual bool parse(Element *elem)
6892         {
6893         if (!parent.getAttribute(elem, "command", command))
6894             return false;
6895         if (!parent.getAttribute(elem, "file", fileName))
6896             return false;
6897         if (!parent.getAttribute(elem, "out", outName))
6898             return false;
6899         std::vector<Element *> children = elem->getChildren();
6900         for (unsigned int i=0 ; i<children.size() ; i++)
6901             {
6902             Element *child = children[i];
6903             String tagName = child->getName();
6904             if (tagName == "flags")
6905                 {
6906                 if (!parent.getValue(child, flags))
6907                     return false;
6908                 }
6909             }
6910         return true;
6911         }
6913 private:
6915     String command;
6916     String flags;
6917     String fileName;
6918     String outName;
6920 };
6924 /**
6925  *  Collect .o's into a .so or DLL
6926  */
6927 class TaskSharedLib : public Task
6929 public:
6931     TaskSharedLib(MakeBase &par) : Task(par)
6932         {
6933         type = TASK_SHAREDLIB; name = "dll";
6934         command = "ar crv";
6935         }
6937     virtual ~TaskSharedLib()
6938         {}
6940     virtual bool execute()
6941         {
6942         //trace("###########HERE %d", fileSet.size());
6943         bool doit = false;
6944         
6945         String fullOut = parent.resolve(fileName);
6946         //trace("ar fullout: %s", fullOut.c_str());
6947         
6948         if (!listFiles(parent, fileSet))
6949             return false;
6950         String fileSetDir = fileSet.getDirectory();
6952         for (unsigned int i=0 ; i<fileSet.size() ; i++)
6953             {
6954             String fname;
6955             if (fileSetDir.size()>0)
6956                 {
6957                 fname.append(fileSetDir);
6958                 fname.append("/");
6959                 }
6960             fname.append(fileSet[i]);
6961             String fullName = parent.resolve(fname);
6962             //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
6963             if (isNewerThan(fullName, fullOut))
6964                 doit = true;
6965             }
6966         //trace("Needs it:%d", doit);
6967         if (!doit)
6968             {
6969             return true;
6970             }
6972         String cmd = "dllwrap";
6973         cmd.append(" -o ");
6974         cmd.append(fullOut);
6975         if (defFileName.size()>0)
6976             {
6977             cmd.append(" --def ");
6978             cmd.append(defFileName);
6979             cmd.append(" ");
6980             }
6981         if (impFileName.size()>0)
6982             {
6983             cmd.append(" --implib ");
6984             cmd.append(impFileName);
6985             cmd.append(" ");
6986             }
6987         for (unsigned int i=0 ; i<fileSet.size() ; i++)
6988             {
6989             String fname;
6990             if (fileSetDir.size()>0)
6991                 {
6992                 fname.append(fileSetDir);
6993                 fname.append("/");
6994                 }
6995             fname.append(fileSet[i]);
6996             String fullName = parent.resolve(fname);
6998             cmd.append(" ");
6999             cmd.append(fullName);
7000             }
7001         cmd.append(" ");
7002         cmd.append(libs);
7004         String outString, errString;
7005         if (!executeCommand(cmd.c_str(), "", outString, errString))
7006             {
7007             error("<sharedlib> problem: %s", errString.c_str());
7008             return false;
7009             }
7011         return true;
7012         }
7014     virtual bool parse(Element *elem)
7015         {
7016         if (!parent.getAttribute(elem, "file", fileName))
7017             return false;
7018         if (!parent.getAttribute(elem, "import", impFileName))
7019             return false;
7020         if (!parent.getAttribute(elem, "def", defFileName))
7021             return false;
7022             
7023         std::vector<Element *> children = elem->getChildren();
7024         for (unsigned int i=0 ; i<children.size() ; i++)
7025             {
7026             Element *child = children[i];
7027             String tagName = child->getName();
7028             if (tagName == "fileset")
7029                 {
7030                 if (!parseFileSet(child, parent, fileSet))
7031                     return false;
7032                 }
7033             else if (tagName == "libs")
7034                 {
7035                 if (!parent.getValue(child, libs))
7036                     return false;
7037                 libs = strip(libs);
7038                 }
7039             }
7040         return true;
7041         }
7043 private:
7045     String command;
7046     String fileName;
7047     String defFileName;
7048     String impFileName;
7049     FileSet fileSet;
7050     String libs;
7052 };
7055 /**
7056  * Run the "ar" command to archive .o's into a .a
7057  */
7058 class TaskStaticLib : public Task
7060 public:
7062     TaskStaticLib(MakeBase &par) : Task(par)
7063         {
7064         type = TASK_STATICLIB; name = "staticlib";
7065         command = "ar crv";
7066         }
7068     virtual ~TaskStaticLib()
7069         {}
7071     virtual bool execute()
7072         {
7073         //trace("###########HERE %d", fileSet.size());
7074         bool doit = false;
7075         
7076         String fullOut = parent.resolve(fileName);
7077         //trace("ar fullout: %s", fullOut.c_str());
7078         
7079         if (!listFiles(parent, fileSet))
7080             return false;
7081         String fileSetDir = fileSet.getDirectory();
7083         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7084             {
7085             String fname;
7086             if (fileSetDir.size()>0)
7087                 {
7088                 fname.append(fileSetDir);
7089                 fname.append("/");
7090                 }
7091             fname.append(fileSet[i]);
7092             String fullName = parent.resolve(fname);
7093             //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
7094             if (isNewerThan(fullName, fullOut))
7095                 doit = true;
7096             }
7097         //trace("Needs it:%d", doit);
7098         if (!doit)
7099             {
7100             return true;
7101             }
7103         String cmd = command;
7104         cmd.append(" ");
7105         cmd.append(fullOut);
7106         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7107             {
7108             String fname;
7109             if (fileSetDir.size()>0)
7110                 {
7111                 fname.append(fileSetDir);
7112                 fname.append("/");
7113                 }
7114             fname.append(fileSet[i]);
7115             String fullName = parent.resolve(fname);
7117             cmd.append(" ");
7118             cmd.append(fullName);
7119             }
7121         String outString, errString;
7122         if (!executeCommand(cmd.c_str(), "", outString, errString))
7123             {
7124             error("<staticlib> problem: %s", errString.c_str());
7125             return false;
7126             }
7128         return true;
7129         }
7131     virtual bool parse(Element *elem)
7132         {
7133         String s;
7134         if (!parent.getAttribute(elem, "command", s))
7135             return false;
7136         if (s.size()>0)
7137             command = s;
7138         if (!parent.getAttribute(elem, "file", fileName))
7139             return false;
7140             
7141         std::vector<Element *> children = elem->getChildren();
7142         for (unsigned int i=0 ; i<children.size() ; i++)
7143             {
7144             Element *child = children[i];
7145             String tagName = child->getName();
7146             if (tagName == "fileset")
7147                 {
7148                 if (!parseFileSet(child, parent, fileSet))
7149                     return false;
7150                 }
7151             }
7152         return true;
7153         }
7155 private:
7157     String command;
7158     String fileName;
7159     FileSet fileSet;
7161 };
7164 /**
7165  * Strip an executable
7166  */
7167 class TaskStrip : public Task
7169 public:
7171     TaskStrip(MakeBase &par) : Task(par)
7172         { type = TASK_STRIP; name = "strip"; }
7174     virtual ~TaskStrip()
7175         {}
7177     virtual bool execute()
7178         {
7179         String fullName = parent.resolve(fileName);
7180         //trace("fullDir:%s", fullDir.c_str());
7181         String cmd;
7182         String outbuf, errbuf;
7184         if (symFileName.size()>0)
7185             {
7186             String symFullName = parent.resolve(symFileName);
7187             cmd = "objcopy --only-keep-debug ";
7188             cmd.append(getNativePath(fullName));
7189             cmd.append(" ");
7190             cmd.append(getNativePath(symFullName));
7191             if (!executeCommand(cmd, "", outbuf, errbuf))
7192                 {
7193                 error("<strip> symbol file failed : %s", errbuf.c_str());
7194                 return false;
7195                 }
7196             }
7197             
7198         cmd = "strip ";
7199         cmd.append(getNativePath(fullName));
7200         if (!executeCommand(cmd, "", outbuf, errbuf))
7201             {
7202             error("<strip> failed : %s", errbuf.c_str());
7203             return false;
7204             }
7205         return true;
7206         }
7208     virtual bool parse(Element *elem)
7209         {
7210         if (!parent.getAttribute(elem, "file", fileName))
7211             return false;
7212         if (!parent.getAttribute(elem, "symfile", symFileName))
7213             return false;
7214         if (fileName.size() == 0)
7215             {
7216             error("<strip> requires 'file=\"fileName\"' attribute");
7217             return false;
7218             }
7219         return true;
7220         }
7222 private:
7224     String fileName;
7225     String symFileName;
7226 };
7229 /**
7230  *
7231  */
7232 class TaskTstamp : public Task
7234 public:
7236     TaskTstamp(MakeBase &par) : Task(par)
7237         { type = TASK_TSTAMP; name = "tstamp"; }
7239     virtual ~TaskTstamp()
7240         {}
7242     virtual bool execute()
7243         {
7244         return true;
7245         }
7247     virtual bool parse(Element *elem)
7248         {
7249         //trace("tstamp parse");
7250         return true;
7251         }
7252 };
7256 /**
7257  *
7258  */
7259 Task *Task::createTask(Element *elem)
7261     String tagName = elem->getName();
7262     //trace("task:%s", tagName.c_str());
7263     Task *task = NULL;
7264     if (tagName == "cc")
7265         task = new TaskCC(parent);
7266     else if (tagName == "copy")
7267         task = new TaskCopy(parent);
7268     else if (tagName == "delete")
7269         task = new TaskDelete(parent);
7270     else if (tagName == "jar")
7271         task = new TaskJar(parent);
7272     else if (tagName == "javac")
7273         task = new TaskJavac(parent);
7274     else if (tagName == "link")
7275         task = new TaskLink(parent);
7276     else if (tagName == "makefile")
7277         task = new TaskMakeFile(parent);
7278     else if (tagName == "mkdir")
7279         task = new TaskMkDir(parent);
7280     else if (tagName == "msgfmt")
7281         task = new TaskMsgFmt(parent);
7282     else if (tagName == "ranlib")
7283         task = new TaskRanlib(parent);
7284     else if (tagName == "rc")
7285         task = new TaskRC(parent);
7286     else if (tagName == "sharedlib")
7287         task = new TaskSharedLib(parent);
7288     else if (tagName == "staticlib")
7289         task = new TaskStaticLib(parent);
7290     else if (tagName == "strip")
7291         task = new TaskStrip(parent);
7292     else if (tagName == "tstamp")
7293         task = new TaskTstamp(parent);
7294     else
7295         {
7296         error("Unknown task '%s'", tagName.c_str());
7297         return NULL;
7298         }
7300     if (!task->parse(elem))
7301         {
7302         delete task;
7303         return NULL;
7304         }
7305     return task;
7310 //########################################################################
7311 //# T A R G E T
7312 //########################################################################
7314 /**
7315  *
7316  */
7317 class Target : public MakeBase
7320 public:
7322     /**
7323      *
7324      */
7325     Target(Make &par) : parent(par)
7326         { init(); }
7328     /**
7329      *
7330      */
7331     Target(const Target &other) : parent(other.parent)
7332         { init(); assign(other); }
7334     /**
7335      *
7336      */
7337     Target &operator=(const Target &other)
7338         { init(); assign(other); return *this; }
7340     /**
7341      *
7342      */
7343     virtual ~Target()
7344         { cleanup() ; }
7347     /**
7348      *
7349      */
7350     virtual Make &getParent()
7351         { return parent; }
7353     /**
7354      *
7355      */
7356     virtual String getName()
7357         { return name; }
7359     /**
7360      *
7361      */
7362     virtual void setName(const String &val)
7363         { name = val; }
7365     /**
7366      *
7367      */
7368     virtual String getDescription()
7369         { return description; }
7371     /**
7372      *
7373      */
7374     virtual void setDescription(const String &val)
7375         { description = val; }
7377     /**
7378      *
7379      */
7380     virtual void addDependency(const String &val)
7381         { deps.push_back(val); }
7383     /**
7384      *
7385      */
7386     virtual void parseDependencies(const String &val)
7387         { deps = tokenize(val, ", "); }
7389     /**
7390      *
7391      */
7392     virtual std::vector<String> &getDependencies()
7393         { return deps; }
7395     /**
7396      *
7397      */
7398     virtual String getIf()
7399         { return ifVar; }
7401     /**
7402      *
7403      */
7404     virtual void setIf(const String &val)
7405         { ifVar = val; }
7407     /**
7408      *
7409      */
7410     virtual String getUnless()
7411         { return unlessVar; }
7413     /**
7414      *
7415      */
7416     virtual void setUnless(const String &val)
7417         { unlessVar = val; }
7419     /**
7420      *
7421      */
7422     virtual void addTask(Task *val)
7423         { tasks.push_back(val); }
7425     /**
7426      *
7427      */
7428     virtual std::vector<Task *> &getTasks()
7429         { return tasks; }
7431 private:
7433     void init()
7434         {
7435         }
7437     void cleanup()
7438         {
7439         tasks.clear();
7440         }
7442     void assign(const Target &other)
7443         {
7444         //parent      = other.parent;
7445         name        = other.name;
7446         description = other.description;
7447         ifVar       = other.ifVar;
7448         unlessVar   = other.unlessVar;
7449         deps        = other.deps;
7450         tasks       = other.tasks;
7451         }
7453     Make &parent;
7455     String name;
7457     String description;
7459     String ifVar;
7461     String unlessVar;
7463     std::vector<String> deps;
7465     std::vector<Task *> tasks;
7467 };
7476 //########################################################################
7477 //# M A K E
7478 //########################################################################
7481 /**
7482  *
7483  */
7484 class Make : public MakeBase
7487 public:
7489     /**
7490      *
7491      */
7492     Make()
7493         { init(); }
7495     /**
7496      *
7497      */
7498     Make(const Make &other)
7499         { assign(other); }
7501     /**
7502      *
7503      */
7504     Make &operator=(const Make &other)
7505         { assign(other); return *this; }
7507     /**
7508      *
7509      */
7510     virtual ~Make()
7511         { cleanup(); }
7513     /**
7514      *
7515      */
7516     virtual std::map<String, Target> &getTargets()
7517         { return targets; }
7520     /**
7521      *
7522      */
7523     virtual String version()
7524         { return "BuildTool v0.6, 2006 Bob Jamison"; }
7526     /**
7527      * Overload a <property>
7528      */
7529     virtual bool specifyProperty(const String &name,
7530                                  const String &value);
7532     /**
7533      *
7534      */
7535     virtual bool run();
7537     /**
7538      *
7539      */
7540     virtual bool run(const String &target);
7544 private:
7546     /**
7547      *
7548      */
7549     void init();
7551     /**
7552      *
7553      */
7554     void cleanup();
7556     /**
7557      *
7558      */
7559     void assign(const Make &other);
7561     /**
7562      *
7563      */
7564     bool executeTask(Task &task);
7567     /**
7568      *
7569      */
7570     bool executeTarget(Target &target,
7571              std::set<String> &targetsCompleted);
7574     /**
7575      *
7576      */
7577     bool execute();
7579     /**
7580      *
7581      */
7582     bool checkTargetDependencies(Target &prop,
7583                     std::vector<String> &depList);
7585     /**
7586      *
7587      */
7588     bool parsePropertyFile(const String &fileName,
7589                            const String &prefix);
7591     /**
7592      *
7593      */
7594     bool parseProperty(Element *elem);
7596     /**
7597      *
7598      */
7599     bool parseTask(Task &task, Element *elem);
7601     /**
7602      *
7603      */
7604     bool parseFile();
7606     /**
7607      *
7608      */
7609     std::vector<String> glob(const String &pattern);
7612     //###############
7613     //# Fields
7614     //###############
7616     String projectName;
7618     String currentTarget;
7620     String defaultTarget;
7622     String specifiedTarget;
7624     String baseDir;
7626     String description;
7627     
7628     String envAlias;
7630     //std::vector<Property> properties;
7631     
7632     std::map<String, Target> targets;
7634     std::vector<Task *> allTasks;
7635     
7636     std::map<String, String> specifiedProperties;
7638 };
7641 //########################################################################
7642 //# C L A S S  M A I N T E N A N C E
7643 //########################################################################
7645 /**
7646  *
7647  */
7648 void Make::init()
7650     uri             = "build.xml";
7651     projectName     = "";
7652     currentTarget   = "";
7653     defaultTarget   = "";
7654     specifiedTarget = "";
7655     baseDir         = "";
7656     description     = "";
7657     envAlias        = "";
7658     properties.clear();
7659     for (unsigned int i = 0 ; i < allTasks.size() ; i++)
7660         delete allTasks[i];
7661     allTasks.clear();
7666 /**
7667  *
7668  */
7669 void Make::cleanup()
7671     for (unsigned int i = 0 ; i < allTasks.size() ; i++)
7672         delete allTasks[i];
7673     allTasks.clear();
7678 /**
7679  *
7680  */
7681 void Make::assign(const Make &other)
7683     uri              = other.uri;
7684     projectName      = other.projectName;
7685     currentTarget    = other.currentTarget;
7686     defaultTarget    = other.defaultTarget;
7687     specifiedTarget  = other.specifiedTarget;
7688     baseDir          = other.baseDir;
7689     description      = other.description;
7690     properties       = other.properties;
7695 //########################################################################
7696 //# U T I L I T Y    T A S K S
7697 //########################################################################
7699 /**
7700  *  Perform a file globbing
7701  */
7702 std::vector<String> Make::glob(const String &pattern)
7704     std::vector<String> res;
7705     return res;
7709 //########################################################################
7710 //# P U B L I C    A P I
7711 //########################################################################
7715 /**
7716  *
7717  */
7718 bool Make::executeTarget(Target &target,
7719              std::set<String> &targetsCompleted)
7722     String name = target.getName();
7724     //First get any dependencies for this target
7725     std::vector<String> deps = target.getDependencies();
7726     for (unsigned int i=0 ; i<deps.size() ; i++)
7727         {
7728         String dep = deps[i];
7729         //Did we do it already?  Skip
7730         if (targetsCompleted.find(dep)!=targetsCompleted.end())
7731             continue;
7732             
7733         std::map<String, Target> &tgts =
7734                target.getParent().getTargets();
7735         std::map<String, Target>::iterator iter =
7736                tgts.find(dep);
7737         if (iter == tgts.end())
7738             {
7739             error("Target '%s' dependency '%s' not found",
7740                       name.c_str(),  dep.c_str());
7741             return false;
7742             }
7743         Target depTarget = iter->second;
7744         if (!executeTarget(depTarget, targetsCompleted))
7745             {
7746             return false;
7747             }
7748         }
7750     status("## Target : %s", name.c_str());
7752     //Now let's do the tasks
7753     std::vector<Task *> &tasks = target.getTasks();
7754     for (unsigned int i=0 ; i<tasks.size() ; i++)
7755         {
7756         Task *task = tasks[i];
7757         status("---- task : %s", task->getName().c_str());
7758         if (!task->execute())
7759             {
7760             return false;
7761             }
7762         }
7763         
7764     targetsCompleted.insert(name);
7765     
7766     return true;
7771 /**
7772  *  Main execute() method.  Start here and work
7773  *  up the dependency tree 
7774  */
7775 bool Make::execute()
7777     status("######## EXECUTE");
7779     //Determine initial target
7780     if (specifiedTarget.size()>0)
7781         {
7782         currentTarget = specifiedTarget;
7783         }
7784     else if (defaultTarget.size()>0)
7785         {
7786         currentTarget = defaultTarget;
7787         }
7788     else
7789         {
7790         error("execute: no specified or default target requested");
7791         return false;
7792         }
7794     std::map<String, Target>::iterator iter =
7795                targets.find(currentTarget);
7796     if (iter == targets.end())
7797         {
7798         error("Initial target '%s' not found",
7799                  currentTarget.c_str());
7800         return false;
7801         }
7802         
7803     //Now run
7804     Target target = iter->second;
7805     std::set<String> targetsCompleted;
7806     if (!executeTarget(target, targetsCompleted))
7807         {
7808         return false;
7809         }
7811     status("######## EXECUTE COMPLETE");
7812     return true;
7818 /**
7819  *
7820  */
7821 bool Make::checkTargetDependencies(Target &target, 
7822                             std::vector<String> &depList)
7824     String tgtName = target.getName().c_str();
7825     depList.push_back(tgtName);
7827     std::vector<String> deps = target.getDependencies();
7828     for (unsigned int i=0 ; i<deps.size() ; i++)
7829         {
7830         String dep = deps[i];
7831         //First thing entered was the starting Target
7832         if (dep == depList[0])
7833             {
7834             error("Circular dependency '%s' found at '%s'",
7835                       dep.c_str(), tgtName.c_str());
7836             std::vector<String>::iterator diter;
7837             for (diter=depList.begin() ; diter!=depList.end() ; diter++)
7838                 {
7839                 error("  %s", diter->c_str());
7840                 }
7841             return false;
7842             }
7844         std::map<String, Target> &tgts =
7845                   target.getParent().getTargets();
7846         std::map<String, Target>::iterator titer = tgts.find(dep);
7847         if (titer == tgts.end())
7848             {
7849             error("Target '%s' dependency '%s' not found",
7850                       tgtName.c_str(), dep.c_str());
7851             return false;
7852             }
7853         if (!checkTargetDependencies(titer->second, depList))
7854             {
7855             return false;
7856             }
7857         }
7858     return true;
7865 static int getword(int pos, const String &inbuf, String &result)
7867     int p = pos;
7868     int len = (int)inbuf.size();
7869     String val;
7870     while (p < len)
7871         {
7872         char ch = inbuf[p];
7873         if (!isalnum(ch) && ch!='.' && ch!='_')
7874             break;
7875         val.push_back(ch);
7876         p++;
7877         }
7878     result = val;
7879     return p;
7885 /**
7886  *
7887  */
7888 bool Make::parsePropertyFile(const String &fileName,
7889                              const String &prefix)
7891     FILE *f = fopen(fileName.c_str(), "r");
7892     if (!f)
7893         {
7894         error("could not open property file %s", fileName.c_str());
7895         return false;
7896         }
7897     int linenr = 0;
7898     while (!feof(f))
7899         {
7900         char buf[256];
7901         if (!fgets(buf, 255, f))
7902             break;
7903         linenr++;
7904         String s = buf;
7905         s = trim(s);
7906         int len = s.size();
7907         if (len == 0)
7908             continue;
7909         if (s[0] == '#')
7910             continue;
7911         String key;
7912         String val;
7913         int p = 0;
7914         int p2 = getword(p, s, key);
7915         if (p2 <= p)
7916             {
7917             error("property file %s, line %d: expected keyword",
7918                     fileName.c_str(), linenr);
7919             return false;
7920             }
7921         if (prefix.size() > 0)
7922             {
7923             key.insert(0, prefix);
7924             }
7926         //skip whitespace
7927         for (p=p2 ; p<len ; p++)
7928             if (!isspace(s[p]))
7929                 break;
7931         if (p>=len || s[p]!='=')
7932             {
7933             error("property file %s, line %d: expected '='",
7934                     fileName.c_str(), linenr);
7935             return false;
7936             }
7937         p++;
7939         //skip whitespace
7940         for ( ; p<len ; p++)
7941             if (!isspace(s[p]))
7942                 break;
7944         /* This way expects a word after the =
7945         p2 = getword(p, s, val);
7946         if (p2 <= p)
7947             {
7948             error("property file %s, line %d: expected value",
7949                     fileName.c_str(), linenr);
7950             return false;
7951             }
7952         */
7953         // This way gets the rest of the line after the =
7954         if (p>=len)
7955             {
7956             error("property file %s, line %d: expected value",
7957                     fileName.c_str(), linenr);
7958             return false;
7959             }
7960         val = s.substr(p);
7961         if (key.size()==0)
7962             continue;
7963         //allow property to be set, even if val=""
7965         //trace("key:'%s' val:'%s'", key.c_str(), val.c_str());
7966         //See if we wanted to overload this property
7967         std::map<String, String>::iterator iter =
7968             specifiedProperties.find(key);
7969         if (iter!=specifiedProperties.end())
7970             {
7971             val = iter->second;
7972             status("overloading property '%s' = '%s'",
7973                    key.c_str(), val.c_str());
7974             }
7975         properties[key] = val;
7976         }
7977     fclose(f);
7978     return true;
7984 /**
7985  *
7986  */
7987 bool Make::parseProperty(Element *elem)
7989     std::vector<Attribute> &attrs = elem->getAttributes();
7990     for (unsigned int i=0 ; i<attrs.size() ; i++)
7991         {
7992         String attrName = attrs[i].getName();
7993         String attrVal  = attrs[i].getValue();
7995         if (attrName == "name")
7996             {
7997             String val;
7998             if (!getAttribute(elem, "value", val))
7999                 return false;
8000             if (val.size() > 0)
8001                 {
8002                 properties[attrVal] = val;
8003                 }
8004             else
8005                 {
8006                 if (!getAttribute(elem, "location", val))
8007                     return false;
8008                 //let the property exist, even if not defined
8009                 properties[attrVal] = val;
8010                 }
8011             //See if we wanted to overload this property
8012             std::map<String, String>::iterator iter =
8013                 specifiedProperties.find(attrVal);
8014             if (iter != specifiedProperties.end())
8015                 {
8016                 val = iter->second;
8017                 status("overloading property '%s' = '%s'",
8018                     attrVal.c_str(), val.c_str());
8019                 properties[attrVal] = val;
8020                 }
8021             }
8022         else if (attrName == "file")
8023             {
8024             String prefix;
8025             if (!getAttribute(elem, "prefix", prefix))
8026                 return false;
8027             if (prefix.size() > 0)
8028                 {
8029                 if (prefix[prefix.size()-1] != '.')
8030                     prefix.push_back('.');
8031                 }
8032             if (!parsePropertyFile(attrName, prefix))
8033                 return false;
8034             }
8035         else if (attrName == "environment")
8036             {
8037             if (envAlias.size() > 0)
8038                 {
8039                 error("environment property can only be set once");
8040                 return false;
8041                 }
8042             envAlias = attrVal;
8043             }
8044         }
8046     return true;
8052 /**
8053  *
8054  */
8055 bool Make::parseFile()
8057     status("######## PARSE : %s", uri.getPath().c_str());
8059     Parser parser;
8060     Element *root = parser.parseFile(uri.getNativePath());
8061     if (!root)
8062         {
8063         error("Could not open %s for reading",
8064               uri.getNativePath().c_str());
8065         return false;
8066         }
8068     if (root->getChildren().size()==0 ||
8069         root->getChildren()[0]->getName()!="project")
8070         {
8071         error("Main xml element should be <project>");
8072         delete root;
8073         return false;
8074         }
8076     //########## Project attributes
8077     Element *project = root->getChildren()[0];
8078     String s = project->getAttribute("name");
8079     if (s.size() > 0)
8080         projectName = s;
8081     s = project->getAttribute("default");
8082     if (s.size() > 0)
8083         defaultTarget = s;
8084     s = project->getAttribute("basedir");
8085     if (s.size() > 0)
8086         baseDir = s;
8088     //######### PARSE MEMBERS
8089     std::vector<Element *> children = project->getChildren();
8090     for (unsigned int i=0 ; i<children.size() ; i++)
8091         {
8092         Element *elem = children[i];
8093         String tagName = elem->getName();
8095         //########## DESCRIPTION
8096         if (tagName == "description")
8097             {
8098             description = parser.trim(elem->getValue());
8099             }
8101         //######### PROPERTY
8102         else if (tagName == "property")
8103             {
8104             if (!parseProperty(elem))
8105                 return false;
8106             }
8108         //######### TARGET
8109         else if (tagName == "target")
8110             {
8111             String tname   = elem->getAttribute("name");
8112             String tdesc   = elem->getAttribute("description");
8113             String tdeps   = elem->getAttribute("depends");
8114             String tif     = elem->getAttribute("if");
8115             String tunless = elem->getAttribute("unless");
8116             Target target(*this);
8117             target.setName(tname);
8118             target.setDescription(tdesc);
8119             target.parseDependencies(tdeps);
8120             target.setIf(tif);
8121             target.setUnless(tunless);
8122             std::vector<Element *> telems = elem->getChildren();
8123             for (unsigned int i=0 ; i<telems.size() ; i++)
8124                 {
8125                 Element *telem = telems[i];
8126                 Task breeder(*this);
8127                 Task *task = breeder.createTask(telem);
8128                 if (!task)
8129                     return false;
8130                 allTasks.push_back(task);
8131                 target.addTask(task);
8132                 }
8134             //Check name
8135             if (tname.size() == 0)
8136                 {
8137                 error("no name for target");
8138                 return false;
8139                 }
8140             //Check for duplicate name
8141             if (targets.find(tname) != targets.end())
8142                 {
8143                 error("target '%s' already defined", tname.c_str());
8144                 return false;
8145                 }
8146             //more work than targets[tname]=target, but avoids default allocator
8147             targets.insert(std::make_pair<String, Target>(tname, target));
8148             }
8150         }
8152     std::map<String, Target>::iterator iter;
8153     for (iter = targets.begin() ; iter!= targets.end() ; iter++)
8154         {
8155         Target tgt = iter->second;
8156         std::vector<String> depList;
8157         if (!checkTargetDependencies(tgt, depList))
8158             {
8159             return false;
8160             }
8161         }
8164     delete root;
8165     status("######## PARSE COMPLETE");
8166     return true;
8170 /**
8171  * Overload a <property>
8172  */
8173 bool Make::specifyProperty(const String &name, const String &value)
8175     if (specifiedProperties.find(name) != specifiedProperties.end())
8176         {
8177         error("Property %s already specified", name.c_str());
8178         return false;
8179         }
8180     specifiedProperties[name] = value;
8181     return true;
8186 /**
8187  *
8188  */
8189 bool Make::run()
8191     if (!parseFile())
8192         return false;
8193         
8194     if (!execute())
8195         return false;
8197     return true;
8203 /**
8204  * Get a formatted MM:SS.sss time elapsed string
8205  */ 
8206 static String
8207 timeDiffString(struct timeval &x, struct timeval &y)
8209     long microsX  = x.tv_usec;
8210     long secondsX = x.tv_sec;
8211     long microsY  = y.tv_usec;
8212     long secondsY = y.tv_sec;
8213     if (microsX < microsY)
8214         {
8215         microsX += 1000000;
8216         secondsX -= 1;
8217         }
8219     int seconds = (int)(secondsX - secondsY);
8220     int millis  = (int)((microsX - microsY)/1000);
8222     int minutes = seconds/60;
8223     seconds -= minutes*60;
8224     char buf[80];
8225     snprintf(buf, 79, "%dm %d.%03ds", minutes, seconds, millis);
8226     String ret = buf;
8227     return ret;
8228     
8231 /**
8232  *
8233  */
8234 bool Make::run(const String &target)
8236     status("####################################################");
8237     status("#   %s", version().c_str());
8238     status("####################################################");
8239     struct timeval timeStart, timeEnd;
8240     ::gettimeofday(&timeStart, NULL);
8241     specifiedTarget = target;
8242     if (!run())
8243         return false;
8244     ::gettimeofday(&timeEnd, NULL);
8245     String timeStr = timeDiffString(timeEnd, timeStart);
8246     status("####################################################");
8247     status("#   BuildTool Completed : %s", timeStr.c_str());
8248     status("####################################################");
8249     return true;
8258 }// namespace buildtool
8259 //########################################################################
8260 //# M A I N
8261 //########################################################################
8263 typedef buildtool::String String;
8265 /**
8266  *  Format an error message in printf() style
8267  */
8268 static void error(char *fmt, ...)
8270     va_list ap;
8271     va_start(ap, fmt);
8272     fprintf(stderr, "BuildTool error: ");
8273     vfprintf(stderr, fmt, ap);
8274     fprintf(stderr, "\n");
8275     va_end(ap);
8279 static bool parseProperty(const String &s, String &name, String &val)
8281     int len = s.size();
8282     int i;
8283     for (i=0 ; i<len ; i++)
8284         {
8285         char ch = s[i];
8286         if (ch == '=')
8287             break;
8288         name.push_back(ch);
8289         }
8290     if (i>=len || s[i]!='=')
8291         {
8292         error("property requires -Dname=value");
8293         return false;
8294         }
8295     i++;
8296     for ( ; i<len ; i++)
8297         {
8298         char ch = s[i];
8299         val.push_back(ch);
8300         }
8301     return true;
8305 /**
8306  * Compare a buffer with a key, for the length of the key
8307  */
8308 static bool sequ(const String &buf, char *key)
8310     int len = buf.size();
8311     for (int i=0 ; key[i] && i<len ; i++)
8312         {
8313         if (key[i] != buf[i])
8314             return false;
8315         }        
8316     return true;
8319 static void usage(int argc, char **argv)
8321     printf("usage:\n");
8322     printf("   %s [options] [target]\n", argv[0]);
8323     printf("Options:\n");
8324     printf("  -help, -h              print this message\n");
8325     printf("  -version               print the version information and exit\n");
8326     printf("  -file <file>           use given buildfile\n");
8327     printf("  -f <file>                 ''\n");
8328     printf("  -D<property>=<value>   use value for given property\n");
8334 /**
8335  * Parse the command-line args, get our options,
8336  * and run this thing
8337  */   
8338 static bool parseOptions(int argc, char **argv)
8340     if (argc < 1)
8341         {
8342         error("Cannot parse arguments");
8343         return false;
8344         }
8346     buildtool::Make make;
8348     String target;
8350     //char *progName = argv[0];
8351     for (int i=1 ; i<argc ; i++)
8352         {
8353         String arg = argv[i];
8354         if (arg.size()>1 && arg[0]=='-')
8355             {
8356             if (arg == "-h" || arg == "-help")
8357                 {
8358                 usage(argc,argv);
8359                 return true;
8360                 }
8361             else if (arg == "-version")
8362                 {
8363                 printf("%s", make.version().c_str());
8364                 return true;
8365                 }
8366             else if (arg == "-f" || arg == "-file")
8367                 {
8368                 if (i>=argc)
8369                    {
8370                    usage(argc, argv);
8371                    return false;
8372                    }
8373                 i++; //eat option
8374                 make.setURI(argv[i]);
8375                 }
8376             else if (arg.size()>2 && sequ(arg, "-D"))
8377                 {
8378                 String s = arg.substr(2, s.size());
8379                 String name, value;
8380                 if (!parseProperty(s, name, value))
8381                    {
8382                    usage(argc, argv);
8383                    return false;
8384                    }
8385                 if (!make.specifyProperty(name, value))
8386                     return false;
8387                 }
8388             else
8389                 {
8390                 error("Unknown option:%s", arg.c_str());
8391                 return false;
8392                 }
8393             }
8394         else
8395             {
8396             if (target.size()>0)
8397                 {
8398                 error("only one initial target");
8399                 usage(argc, argv);
8400                 return false;
8401                 }
8402             target = arg;
8403             }
8404         }
8406     //We have the options.  Now execute them
8407     if (!make.run(target))
8408         return false;
8410     return true;
8416 /*
8417 static bool runMake()
8419     buildtool::Make make;
8420     if (!make.run())
8421         return false;
8422     return true;
8426 static bool pkgConfigTest()
8428     buildtool::PkgConfig pkgConfig;
8429     if (!pkgConfig.readFile("gtk+-2.0.pc"))
8430         return false;
8431     return true;
8436 static bool depTest()
8438     buildtool::DepTool deptool;
8439     deptool.setSourceDirectory("/dev/ink/inkscape/src");
8440     if (!deptool.generateDependencies("build.dep"))
8441         return false;
8442     std::vector<buildtool::DepRec> res =
8443            deptool.loadDepFile("build.dep");
8444     if (res.size() == 0)
8445         return false;
8446     return true;
8449 static bool popenTest()
8451     buildtool::Make make;
8452     buildtool::String out, err;
8453     bool ret = make.executeCommand("gcc xx.cpp", "", out, err);
8454     printf("Popen test:%d '%s' '%s'\n", ret, out.c_str(), err.c_str());
8455     return true;
8459 static bool propFileTest()
8461     buildtool::Make make;
8462     make.parsePropertyFile("test.prop", "test.");
8463     return true;
8465 */
8467 int main(int argc, char **argv)
8470     if (!parseOptions(argc, argv))
8471         return 1;
8472     /*
8473     if (!popenTest())
8474         return 1;
8476     if (!depTest())
8477         return 1;
8478     if (!propFileTest())
8479         return 1;
8480     if (runMake())
8481         return 1;
8482     */
8483     return 0;
8487 //########################################################################
8488 //# E N D 
8489 //########################################################################