Code

get gettimeofday() out of buildtool:: namespace
[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          }
6668     virtual ~TaskMsgFmt()
6669         {}
6671     virtual bool execute()
6672         {
6673         if (!listFiles(parent, fileSet))
6674             return false;
6675         String fileSetDir = fileSet.getDirectory();
6677         //trace("msgfmt: %d", fileSet.size());
6678         for (unsigned int i=0 ; i<fileSet.size() ; i++)
6679             {
6680             String fileName = fileSet[i];
6681             if (getSuffix(fileName) != "po")
6682                 continue;
6683             String sourcePath;
6684             if (fileSetDir.size()>0)
6685                 {
6686                 sourcePath.append(fileSetDir);
6687                 sourcePath.append("/");
6688                 }
6689             sourcePath.append(fileName);
6690             String fullSource = parent.resolve(sourcePath);
6692             String destPath;
6693             if (toDirName.size()>0)
6694                 {
6695                 destPath.append(toDirName);
6696                 destPath.append("/");
6697                 }
6698             if (owndir)
6699                 {
6700                 String subdir = fileName;
6701                 unsigned int pos = subdir.find_last_of('.');
6702                 if (pos != subdir.npos)
6703                     subdir = subdir.substr(0, pos);
6704                 destPath.append(subdir);
6705                 destPath.append("/");
6706                 }
6707             destPath.append(fileName);
6708             destPath[destPath.size()-2] = 'm';
6709             String fullDest = parent.resolve(destPath);
6711             if (!isNewerThan(fullSource, fullDest))
6712                 {
6713                 //trace("skip %s", fullSource.c_str());
6714                 continue;
6715                 }
6716                 
6717             String cmd = command;
6718             cmd.append(" ");
6719             cmd.append(fullSource);
6720             cmd.append(" -o ");
6721             cmd.append(fullDest);
6722             
6723             int pos = fullDest.find_last_of('/');
6724             if (pos>0)
6725                 {
6726                 String fullDestPath = fullDest.substr(0, pos);
6727                 if (!createDirectory(fullDestPath))
6728                     return false;
6729                 }
6733             String outString, errString;
6734             if (!executeCommand(cmd.c_str(), "", outString, errString))
6735                 {
6736                 error("<msgfmt> problem: %s", errString.c_str());
6737                 return false;
6738                 }
6739             }
6741         return true;
6742         }
6744     virtual bool parse(Element *elem)
6745         {
6746         String s;
6747         if (!parent.getAttribute(elem, "command", s))
6748             return false;
6749         if (s.size()>0)
6750             command = s;
6751         if (!parent.getAttribute(elem, "todir", toDirName))
6752             return false;
6753         if (!parent.getAttribute(elem, "owndir", s))
6754             return false;
6755         if (!getBool(s, owndir))
6756             return false;
6757             
6758         std::vector<Element *> children = elem->getChildren();
6759         for (unsigned int i=0 ; i<children.size() ; i++)
6760             {
6761             Element *child = children[i];
6762             String tagName = child->getName();
6763             if (tagName == "fileset")
6764                 {
6765                 if (!parseFileSet(child, parent, fileSet))
6766                     return false;
6767                 }
6768             }
6769         return true;
6770         }
6772 private:
6774     String command;
6775     String toDirName;
6776     FileSet fileSet;
6777     bool owndir;
6779 };
6785 /**
6786  *  Process an archive to allow random access
6787  */
6788 class TaskRanlib : public Task
6790 public:
6792     TaskRanlib(MakeBase &par) : Task(par)
6793         {
6794         type = TASK_RANLIB; name = "ranlib";
6795         command = "ranlib";
6796         }
6798     virtual ~TaskRanlib()
6799         {}
6801     virtual bool execute()
6802         {
6803         String fullName = parent.resolve(fileName);
6804         //trace("fullDir:%s", fullDir.c_str());
6805         String cmd = command;
6806         cmd.append(" ");
6807         cmd.append(fullName);
6808         String outbuf, errbuf;
6809         if (!executeCommand(cmd, "", outbuf, errbuf))
6810             return false;
6811         return true;
6812         }
6814     virtual bool parse(Element *elem)
6815         {
6816         String s;
6817         if (!parent.getAttribute(elem, "command", s))
6818             return false;
6819         if (s.size()>0)
6820            command = s;
6821         if (!parent.getAttribute(elem, "file", fileName))
6822             return false;
6823         if (fileName.size() == 0)
6824             {
6825             error("<ranlib> requires 'file=\"fileNname\"' attribute");
6826             return false;
6827             }
6828         return true;
6829         }
6831 private:
6833     String fileName;
6834     String command;
6835 };
6839 /**
6840  * Run the "ar" command to archive .o's into a .a
6841  */
6842 class TaskRC : public Task
6844 public:
6846     TaskRC(MakeBase &par) : Task(par)
6847         {
6848         type = TASK_RC; name = "rc";
6849         command = "windres";
6850         }
6852     virtual ~TaskRC()
6853         {}
6855     virtual bool execute()
6856         {
6857         String fullFile = parent.resolve(fileName);
6858         String fullOut  = parent.resolve(outName);
6859         if (!isNewerThan(fullFile, fullOut))
6860             return true;
6861         String cmd = command;
6862         cmd.append(" -o ");
6863         cmd.append(fullOut);
6864         cmd.append(" ");
6865         cmd.append(flags);
6866         cmd.append(" ");
6867         cmd.append(fullFile);
6869         String outString, errString;
6870         if (!executeCommand(cmd.c_str(), "", outString, errString))
6871             {
6872             error("RC problem: %s", errString.c_str());
6873             return false;
6874             }
6875         return true;
6876         }
6878     virtual bool parse(Element *elem)
6879         {
6880         if (!parent.getAttribute(elem, "command", command))
6881             return false;
6882         if (!parent.getAttribute(elem, "file", fileName))
6883             return false;
6884         if (!parent.getAttribute(elem, "out", outName))
6885             return false;
6886         std::vector<Element *> children = elem->getChildren();
6887         for (unsigned int i=0 ; i<children.size() ; i++)
6888             {
6889             Element *child = children[i];
6890             String tagName = child->getName();
6891             if (tagName == "flags")
6892                 {
6893                 if (!parent.getValue(child, flags))
6894                     return false;
6895                 }
6896             }
6897         return true;
6898         }
6900 private:
6902     String command;
6903     String flags;
6904     String fileName;
6905     String outName;
6907 };
6911 /**
6912  *  Collect .o's into a .so or DLL
6913  */
6914 class TaskSharedLib : public Task
6916 public:
6918     TaskSharedLib(MakeBase &par) : Task(par)
6919         {
6920         type = TASK_SHAREDLIB; name = "dll";
6921         command = "ar crv";
6922         }
6924     virtual ~TaskSharedLib()
6925         {}
6927     virtual bool execute()
6928         {
6929         //trace("###########HERE %d", fileSet.size());
6930         bool doit = false;
6931         
6932         String fullOut = parent.resolve(fileName);
6933         //trace("ar fullout: %s", fullOut.c_str());
6934         
6935         if (!listFiles(parent, fileSet))
6936             return false;
6937         String fileSetDir = fileSet.getDirectory();
6939         for (unsigned int i=0 ; i<fileSet.size() ; i++)
6940             {
6941             String fname;
6942             if (fileSetDir.size()>0)
6943                 {
6944                 fname.append(fileSetDir);
6945                 fname.append("/");
6946                 }
6947             fname.append(fileSet[i]);
6948             String fullName = parent.resolve(fname);
6949             //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
6950             if (isNewerThan(fullName, fullOut))
6951                 doit = true;
6952             }
6953         //trace("Needs it:%d", doit);
6954         if (!doit)
6955             {
6956             return true;
6957             }
6959         String cmd = "dllwrap";
6960         cmd.append(" -o ");
6961         cmd.append(fullOut);
6962         if (defFileName.size()>0)
6963             {
6964             cmd.append(" --def ");
6965             cmd.append(defFileName);
6966             cmd.append(" ");
6967             }
6968         if (impFileName.size()>0)
6969             {
6970             cmd.append(" --implib ");
6971             cmd.append(impFileName);
6972             cmd.append(" ");
6973             }
6974         for (unsigned int i=0 ; i<fileSet.size() ; i++)
6975             {
6976             String fname;
6977             if (fileSetDir.size()>0)
6978                 {
6979                 fname.append(fileSetDir);
6980                 fname.append("/");
6981                 }
6982             fname.append(fileSet[i]);
6983             String fullName = parent.resolve(fname);
6985             cmd.append(" ");
6986             cmd.append(fullName);
6987             }
6988         cmd.append(" ");
6989         cmd.append(libs);
6991         String outString, errString;
6992         if (!executeCommand(cmd.c_str(), "", outString, errString))
6993             {
6994             error("<sharedlib> problem: %s", errString.c_str());
6995             return false;
6996             }
6998         return true;
6999         }
7001     virtual bool parse(Element *elem)
7002         {
7003         if (!parent.getAttribute(elem, "file", fileName))
7004             return false;
7005         if (!parent.getAttribute(elem, "import", impFileName))
7006             return false;
7007         if (!parent.getAttribute(elem, "def", defFileName))
7008             return false;
7009             
7010         std::vector<Element *> children = elem->getChildren();
7011         for (unsigned int i=0 ; i<children.size() ; i++)
7012             {
7013             Element *child = children[i];
7014             String tagName = child->getName();
7015             if (tagName == "fileset")
7016                 {
7017                 if (!parseFileSet(child, parent, fileSet))
7018                     return false;
7019                 }
7020             else if (tagName == "libs")
7021                 {
7022                 if (!parent.getValue(child, libs))
7023                     return false;
7024                 libs = strip(libs);
7025                 }
7026             }
7027         return true;
7028         }
7030 private:
7032     String command;
7033     String fileName;
7034     String defFileName;
7035     String impFileName;
7036     FileSet fileSet;
7037     String libs;
7039 };
7042 /**
7043  * Run the "ar" command to archive .o's into a .a
7044  */
7045 class TaskStaticLib : public Task
7047 public:
7049     TaskStaticLib(MakeBase &par) : Task(par)
7050         {
7051         type = TASK_STATICLIB; name = "staticlib";
7052         command = "ar crv";
7053         }
7055     virtual ~TaskStaticLib()
7056         {}
7058     virtual bool execute()
7059         {
7060         //trace("###########HERE %d", fileSet.size());
7061         bool doit = false;
7062         
7063         String fullOut = parent.resolve(fileName);
7064         //trace("ar fullout: %s", fullOut.c_str());
7065         
7066         if (!listFiles(parent, fileSet))
7067             return false;
7068         String fileSetDir = fileSet.getDirectory();
7070         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7071             {
7072             String fname;
7073             if (fileSetDir.size()>0)
7074                 {
7075                 fname.append(fileSetDir);
7076                 fname.append("/");
7077                 }
7078             fname.append(fileSet[i]);
7079             String fullName = parent.resolve(fname);
7080             //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
7081             if (isNewerThan(fullName, fullOut))
7082                 doit = true;
7083             }
7084         //trace("Needs it:%d", doit);
7085         if (!doit)
7086             {
7087             return true;
7088             }
7090         String cmd = command;
7091         cmd.append(" ");
7092         cmd.append(fullOut);
7093         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7094             {
7095             String fname;
7096             if (fileSetDir.size()>0)
7097                 {
7098                 fname.append(fileSetDir);
7099                 fname.append("/");
7100                 }
7101             fname.append(fileSet[i]);
7102             String fullName = parent.resolve(fname);
7104             cmd.append(" ");
7105             cmd.append(fullName);
7106             }
7108         String outString, errString;
7109         if (!executeCommand(cmd.c_str(), "", outString, errString))
7110             {
7111             error("<staticlib> problem: %s", errString.c_str());
7112             return false;
7113             }
7115         return true;
7116         }
7118     virtual bool parse(Element *elem)
7119         {
7120         String s;
7121         if (!parent.getAttribute(elem, "command", s))
7122             return false;
7123         if (s.size()>0)
7124             command = s;
7125         if (!parent.getAttribute(elem, "file", fileName))
7126             return false;
7127             
7128         std::vector<Element *> children = elem->getChildren();
7129         for (unsigned int i=0 ; i<children.size() ; i++)
7130             {
7131             Element *child = children[i];
7132             String tagName = child->getName();
7133             if (tagName == "fileset")
7134                 {
7135                 if (!parseFileSet(child, parent, fileSet))
7136                     return false;
7137                 }
7138             }
7139         return true;
7140         }
7142 private:
7144     String command;
7145     String fileName;
7146     FileSet fileSet;
7148 };
7151 /**
7152  * Strip an executable
7153  */
7154 class TaskStrip : public Task
7156 public:
7158     TaskStrip(MakeBase &par) : Task(par)
7159         { type = TASK_STRIP; name = "strip"; }
7161     virtual ~TaskStrip()
7162         {}
7164     virtual bool execute()
7165         {
7166         String fullName = parent.resolve(fileName);
7167         //trace("fullDir:%s", fullDir.c_str());
7168         String cmd;
7169         String outbuf, errbuf;
7171         if (symFileName.size()>0)
7172             {
7173             String symFullName = parent.resolve(symFileName);
7174             cmd = "objcopy --only-keep-debug ";
7175             cmd.append(getNativePath(fullName));
7176             cmd.append(" ");
7177             cmd.append(getNativePath(symFullName));
7178             if (!executeCommand(cmd, "", outbuf, errbuf))
7179                 {
7180                 error("<strip> symbol file failed : %s", errbuf.c_str());
7181                 return false;
7182                 }
7183             }
7184             
7185         cmd = "strip ";
7186         cmd.append(getNativePath(fullName));
7187         if (!executeCommand(cmd, "", outbuf, errbuf))
7188             {
7189             error("<strip> failed : %s", errbuf.c_str());
7190             return false;
7191             }
7192         return true;
7193         }
7195     virtual bool parse(Element *elem)
7196         {
7197         if (!parent.getAttribute(elem, "file", fileName))
7198             return false;
7199         if (!parent.getAttribute(elem, "symfile", symFileName))
7200             return false;
7201         if (fileName.size() == 0)
7202             {
7203             error("<strip> requires 'file=\"fileName\"' attribute");
7204             return false;
7205             }
7206         return true;
7207         }
7209 private:
7211     String fileName;
7212     String symFileName;
7213 };
7216 /**
7217  *
7218  */
7219 class TaskTstamp : public Task
7221 public:
7223     TaskTstamp(MakeBase &par) : Task(par)
7224         { type = TASK_TSTAMP; name = "tstamp"; }
7226     virtual ~TaskTstamp()
7227         {}
7229     virtual bool execute()
7230         {
7231         return true;
7232         }
7234     virtual bool parse(Element *elem)
7235         {
7236         //trace("tstamp parse");
7237         return true;
7238         }
7239 };
7243 /**
7244  *
7245  */
7246 Task *Task::createTask(Element *elem)
7248     String tagName = elem->getName();
7249     //trace("task:%s", tagName.c_str());
7250     Task *task = NULL;
7251     if (tagName == "cc")
7252         task = new TaskCC(parent);
7253     else if (tagName == "copy")
7254         task = new TaskCopy(parent);
7255     else if (tagName == "delete")
7256         task = new TaskDelete(parent);
7257     else if (tagName == "jar")
7258         task = new TaskJar(parent);
7259     else if (tagName == "javac")
7260         task = new TaskJavac(parent);
7261     else if (tagName == "link")
7262         task = new TaskLink(parent);
7263     else if (tagName == "makefile")
7264         task = new TaskMakeFile(parent);
7265     else if (tagName == "mkdir")
7266         task = new TaskMkDir(parent);
7267     else if (tagName == "msgfmt")
7268         task = new TaskMsgFmt(parent);
7269     else if (tagName == "ranlib")
7270         task = new TaskRanlib(parent);
7271     else if (tagName == "rc")
7272         task = new TaskRC(parent);
7273     else if (tagName == "sharedlib")
7274         task = new TaskSharedLib(parent);
7275     else if (tagName == "staticlib")
7276         task = new TaskStaticLib(parent);
7277     else if (tagName == "strip")
7278         task = new TaskStrip(parent);
7279     else if (tagName == "tstamp")
7280         task = new TaskTstamp(parent);
7281     else
7282         {
7283         error("Unknown task '%s'", tagName.c_str());
7284         return NULL;
7285         }
7287     if (!task->parse(elem))
7288         {
7289         delete task;
7290         return NULL;
7291         }
7292     return task;
7297 //########################################################################
7298 //# T A R G E T
7299 //########################################################################
7301 /**
7302  *
7303  */
7304 class Target : public MakeBase
7307 public:
7309     /**
7310      *
7311      */
7312     Target(Make &par) : parent(par)
7313         { init(); }
7315     /**
7316      *
7317      */
7318     Target(const Target &other) : parent(other.parent)
7319         { init(); assign(other); }
7321     /**
7322      *
7323      */
7324     Target &operator=(const Target &other)
7325         { init(); assign(other); return *this; }
7327     /**
7328      *
7329      */
7330     virtual ~Target()
7331         { cleanup() ; }
7334     /**
7335      *
7336      */
7337     virtual Make &getParent()
7338         { return parent; }
7340     /**
7341      *
7342      */
7343     virtual String getName()
7344         { return name; }
7346     /**
7347      *
7348      */
7349     virtual void setName(const String &val)
7350         { name = val; }
7352     /**
7353      *
7354      */
7355     virtual String getDescription()
7356         { return description; }
7358     /**
7359      *
7360      */
7361     virtual void setDescription(const String &val)
7362         { description = val; }
7364     /**
7365      *
7366      */
7367     virtual void addDependency(const String &val)
7368         { deps.push_back(val); }
7370     /**
7371      *
7372      */
7373     virtual void parseDependencies(const String &val)
7374         { deps = tokenize(val, ", "); }
7376     /**
7377      *
7378      */
7379     virtual std::vector<String> &getDependencies()
7380         { return deps; }
7382     /**
7383      *
7384      */
7385     virtual String getIf()
7386         { return ifVar; }
7388     /**
7389      *
7390      */
7391     virtual void setIf(const String &val)
7392         { ifVar = val; }
7394     /**
7395      *
7396      */
7397     virtual String getUnless()
7398         { return unlessVar; }
7400     /**
7401      *
7402      */
7403     virtual void setUnless(const String &val)
7404         { unlessVar = val; }
7406     /**
7407      *
7408      */
7409     virtual void addTask(Task *val)
7410         { tasks.push_back(val); }
7412     /**
7413      *
7414      */
7415     virtual std::vector<Task *> &getTasks()
7416         { return tasks; }
7418 private:
7420     void init()
7421         {
7422         }
7424     void cleanup()
7425         {
7426         tasks.clear();
7427         }
7429     void assign(const Target &other)
7430         {
7431         //parent      = other.parent;
7432         name        = other.name;
7433         description = other.description;
7434         ifVar       = other.ifVar;
7435         unlessVar   = other.unlessVar;
7436         deps        = other.deps;
7437         tasks       = other.tasks;
7438         }
7440     Make &parent;
7442     String name;
7444     String description;
7446     String ifVar;
7448     String unlessVar;
7450     std::vector<String> deps;
7452     std::vector<Task *> tasks;
7454 };
7463 //########################################################################
7464 //# M A K E
7465 //########################################################################
7468 /**
7469  *
7470  */
7471 class Make : public MakeBase
7474 public:
7476     /**
7477      *
7478      */
7479     Make()
7480         { init(); }
7482     /**
7483      *
7484      */
7485     Make(const Make &other)
7486         { assign(other); }
7488     /**
7489      *
7490      */
7491     Make &operator=(const Make &other)
7492         { assign(other); return *this; }
7494     /**
7495      *
7496      */
7497     virtual ~Make()
7498         { cleanup(); }
7500     /**
7501      *
7502      */
7503     virtual std::map<String, Target> &getTargets()
7504         { return targets; }
7507     /**
7508      *
7509      */
7510     virtual String version()
7511         { return "BuildTool v0.6, 2006 Bob Jamison"; }
7513     /**
7514      * Overload a <property>
7515      */
7516     virtual bool specifyProperty(const String &name,
7517                                  const String &value);
7519     /**
7520      *
7521      */
7522     virtual bool run();
7524     /**
7525      *
7526      */
7527     virtual bool run(const String &target);
7531 private:
7533     /**
7534      *
7535      */
7536     void init();
7538     /**
7539      *
7540      */
7541     void cleanup();
7543     /**
7544      *
7545      */
7546     void assign(const Make &other);
7548     /**
7549      *
7550      */
7551     bool executeTask(Task &task);
7554     /**
7555      *
7556      */
7557     bool executeTarget(Target &target,
7558              std::set<String> &targetsCompleted);
7561     /**
7562      *
7563      */
7564     bool execute();
7566     /**
7567      *
7568      */
7569     bool checkTargetDependencies(Target &prop,
7570                     std::vector<String> &depList);
7572     /**
7573      *
7574      */
7575     bool parsePropertyFile(const String &fileName,
7576                            const String &prefix);
7578     /**
7579      *
7580      */
7581     bool parseProperty(Element *elem);
7583     /**
7584      *
7585      */
7586     bool parseTask(Task &task, Element *elem);
7588     /**
7589      *
7590      */
7591     bool parseFile();
7593     /**
7594      *
7595      */
7596     std::vector<String> glob(const String &pattern);
7599     //###############
7600     //# Fields
7601     //###############
7603     String projectName;
7605     String currentTarget;
7607     String defaultTarget;
7609     String specifiedTarget;
7611     String baseDir;
7613     String description;
7614     
7615     String envAlias;
7617     //std::vector<Property> properties;
7618     
7619     std::map<String, Target> targets;
7621     std::vector<Task *> allTasks;
7622     
7623     std::map<String, String> specifiedProperties;
7625 };
7628 //########################################################################
7629 //# C L A S S  M A I N T E N A N C E
7630 //########################################################################
7632 /**
7633  *
7634  */
7635 void Make::init()
7637     uri             = "build.xml";
7638     projectName     = "";
7639     currentTarget   = "";
7640     defaultTarget   = "";
7641     specifiedTarget = "";
7642     baseDir         = "";
7643     description     = "";
7644     envAlias        = "";
7645     properties.clear();
7646     for (unsigned int i = 0 ; i < allTasks.size() ; i++)
7647         delete allTasks[i];
7648     allTasks.clear();
7653 /**
7654  *
7655  */
7656 void Make::cleanup()
7658     for (unsigned int i = 0 ; i < allTasks.size() ; i++)
7659         delete allTasks[i];
7660     allTasks.clear();
7665 /**
7666  *
7667  */
7668 void Make::assign(const Make &other)
7670     uri              = other.uri;
7671     projectName      = other.projectName;
7672     currentTarget    = other.currentTarget;
7673     defaultTarget    = other.defaultTarget;
7674     specifiedTarget  = other.specifiedTarget;
7675     baseDir          = other.baseDir;
7676     description      = other.description;
7677     properties       = other.properties;
7682 //########################################################################
7683 //# U T I L I T Y    T A S K S
7684 //########################################################################
7686 /**
7687  *  Perform a file globbing
7688  */
7689 std::vector<String> Make::glob(const String &pattern)
7691     std::vector<String> res;
7692     return res;
7696 //########################################################################
7697 //# P U B L I C    A P I
7698 //########################################################################
7702 /**
7703  *
7704  */
7705 bool Make::executeTarget(Target &target,
7706              std::set<String> &targetsCompleted)
7709     String name = target.getName();
7711     //First get any dependencies for this target
7712     std::vector<String> deps = target.getDependencies();
7713     for (unsigned int i=0 ; i<deps.size() ; i++)
7714         {
7715         String dep = deps[i];
7716         //Did we do it already?  Skip
7717         if (targetsCompleted.find(dep)!=targetsCompleted.end())
7718             continue;
7719             
7720         std::map<String, Target> &tgts =
7721                target.getParent().getTargets();
7722         std::map<String, Target>::iterator iter =
7723                tgts.find(dep);
7724         if (iter == tgts.end())
7725             {
7726             error("Target '%s' dependency '%s' not found",
7727                       name.c_str(),  dep.c_str());
7728             return false;
7729             }
7730         Target depTarget = iter->second;
7731         if (!executeTarget(depTarget, targetsCompleted))
7732             {
7733             return false;
7734             }
7735         }
7737     status("## Target : %s", name.c_str());
7739     //Now let's do the tasks
7740     std::vector<Task *> &tasks = target.getTasks();
7741     for (unsigned int i=0 ; i<tasks.size() ; i++)
7742         {
7743         Task *task = tasks[i];
7744         status("---- task : %s", task->getName().c_str());
7745         if (!task->execute())
7746             {
7747             return false;
7748             }
7749         }
7750         
7751     targetsCompleted.insert(name);
7752     
7753     return true;
7758 /**
7759  *  Main execute() method.  Start here and work
7760  *  up the dependency tree 
7761  */
7762 bool Make::execute()
7764     status("######## EXECUTE");
7766     //Determine initial target
7767     if (specifiedTarget.size()>0)
7768         {
7769         currentTarget = specifiedTarget;
7770         }
7771     else if (defaultTarget.size()>0)
7772         {
7773         currentTarget = defaultTarget;
7774         }
7775     else
7776         {
7777         error("execute: no specified or default target requested");
7778         return false;
7779         }
7781     std::map<String, Target>::iterator iter =
7782                targets.find(currentTarget);
7783     if (iter == targets.end())
7784         {
7785         error("Initial target '%s' not found",
7786                  currentTarget.c_str());
7787         return false;
7788         }
7789         
7790     //Now run
7791     Target target = iter->second;
7792     std::set<String> targetsCompleted;
7793     if (!executeTarget(target, targetsCompleted))
7794         {
7795         return false;
7796         }
7798     status("######## EXECUTE COMPLETE");
7799     return true;
7805 /**
7806  *
7807  */
7808 bool Make::checkTargetDependencies(Target &target, 
7809                             std::vector<String> &depList)
7811     String tgtName = target.getName().c_str();
7812     depList.push_back(tgtName);
7814     std::vector<String> deps = target.getDependencies();
7815     for (unsigned int i=0 ; i<deps.size() ; i++)
7816         {
7817         String dep = deps[i];
7818         //First thing entered was the starting Target
7819         if (dep == depList[0])
7820             {
7821             error("Circular dependency '%s' found at '%s'",
7822                       dep.c_str(), tgtName.c_str());
7823             std::vector<String>::iterator diter;
7824             for (diter=depList.begin() ; diter!=depList.end() ; diter++)
7825                 {
7826                 error("  %s", diter->c_str());
7827                 }
7828             return false;
7829             }
7831         std::map<String, Target> &tgts =
7832                   target.getParent().getTargets();
7833         std::map<String, Target>::iterator titer = tgts.find(dep);
7834         if (titer == tgts.end())
7835             {
7836             error("Target '%s' dependency '%s' not found",
7837                       tgtName.c_str(), dep.c_str());
7838             return false;
7839             }
7840         if (!checkTargetDependencies(titer->second, depList))
7841             {
7842             return false;
7843             }
7844         }
7845     return true;
7852 static int getword(int pos, const String &inbuf, String &result)
7854     int p = pos;
7855     int len = (int)inbuf.size();
7856     String val;
7857     while (p < len)
7858         {
7859         char ch = inbuf[p];
7860         if (!isalnum(ch) && ch!='.' && ch!='_')
7861             break;
7862         val.push_back(ch);
7863         p++;
7864         }
7865     result = val;
7866     return p;
7872 /**
7873  *
7874  */
7875 bool Make::parsePropertyFile(const String &fileName,
7876                              const String &prefix)
7878     FILE *f = fopen(fileName.c_str(), "r");
7879     if (!f)
7880         {
7881         error("could not open property file %s", fileName.c_str());
7882         return false;
7883         }
7884     int linenr = 0;
7885     while (!feof(f))
7886         {
7887         char buf[256];
7888         if (!fgets(buf, 255, f))
7889             break;
7890         linenr++;
7891         String s = buf;
7892         s = trim(s);
7893         int len = s.size();
7894         if (len == 0)
7895             continue;
7896         if (s[0] == '#')
7897             continue;
7898         String key;
7899         String val;
7900         int p = 0;
7901         int p2 = getword(p, s, key);
7902         if (p2 <= p)
7903             {
7904             error("property file %s, line %d: expected keyword",
7905                     fileName.c_str(), linenr);
7906             return false;
7907             }
7908         if (prefix.size() > 0)
7909             {
7910             key.insert(0, prefix);
7911             }
7913         //skip whitespace
7914         for (p=p2 ; p<len ; p++)
7915             if (!isspace(s[p]))
7916                 break;
7918         if (p>=len || s[p]!='=')
7919             {
7920             error("property file %s, line %d: expected '='",
7921                     fileName.c_str(), linenr);
7922             return false;
7923             }
7924         p++;
7926         //skip whitespace
7927         for ( ; p<len ; p++)
7928             if (!isspace(s[p]))
7929                 break;
7931         /* This way expects a word after the =
7932         p2 = getword(p, s, val);
7933         if (p2 <= p)
7934             {
7935             error("property file %s, line %d: expected value",
7936                     fileName.c_str(), linenr);
7937             return false;
7938             }
7939         */
7940         // This way gets the rest of the line after the =
7941         if (p>=len)
7942             {
7943             error("property file %s, line %d: expected value",
7944                     fileName.c_str(), linenr);
7945             return false;
7946             }
7947         val = s.substr(p);
7948         if (key.size()==0 || val.size()==0)
7949             continue;
7951         //trace("key:'%s' val:'%s'", key.c_str(), val.c_str());
7952         //See if we wanted to overload this property
7953         std::map<String, String>::iterator iter =
7954             specifiedProperties.find(key);
7955         if (iter!=specifiedProperties.end())
7956             {
7957             val = iter->second;
7958             status("overloading property '%s' = '%s'",
7959                    key.c_str(), val.c_str());
7960             }
7961         properties[key] = val;
7962         }
7963     fclose(f);
7964     return true;
7970 /**
7971  *
7972  */
7973 bool Make::parseProperty(Element *elem)
7975     std::vector<Attribute> &attrs = elem->getAttributes();
7976     for (unsigned int i=0 ; i<attrs.size() ; i++)
7977         {
7978         String attrName = attrs[i].getName();
7979         String attrVal  = attrs[i].getValue();
7981         if (attrName == "name")
7982             {
7983             String val;
7984             if (!getAttribute(elem, "value", val))
7985                 return false;
7986             if (val.size() > 0)
7987                 {
7988                 properties[attrVal] = val;
7989                 }
7990             else
7991                 {
7992                 if (!getAttribute(elem, "location", val))
7993                     return false;
7994                 if (val.size() > 0)
7995                     {
7996                     properties[attrVal] = val;
7997                     }
7998                 }
7999             //See if we wanted to overload this property
8000             std::map<String, String>::iterator iter =
8001                 specifiedProperties.find(attrVal);
8002             if (iter != specifiedProperties.end())
8003                 {
8004                 val = iter->second;
8005                 status("overloading property '%s' = '%s'",
8006                     attrVal.c_str(), val.c_str());
8007                 properties[attrVal] = val;
8008                 }
8009             }
8010         else if (attrName == "file")
8011             {
8012             String prefix;
8013             if (!getAttribute(elem, "prefix", prefix))
8014                 return false;
8015             if (prefix.size() > 0)
8016                 {
8017                 if (prefix[prefix.size()-1] != '.')
8018                     prefix.push_back('.');
8019                 }
8020             if (!parsePropertyFile(attrName, prefix))
8021                 return false;
8022             }
8023         else if (attrName == "environment")
8024             {
8025             if (envAlias.size() > 0)
8026                 {
8027                 error("environment property can only be set once");
8028                 return false;
8029                 }
8030             envAlias = attrVal;
8031             }
8032         }
8034     return true;
8040 /**
8041  *
8042  */
8043 bool Make::parseFile()
8045     status("######## PARSE : %s", uri.getPath().c_str());
8047     Parser parser;
8048     Element *root = parser.parseFile(uri.getNativePath());
8049     if (!root)
8050         {
8051         error("Could not open %s for reading",
8052               uri.getNativePath().c_str());
8053         return false;
8054         }
8056     if (root->getChildren().size()==0 ||
8057         root->getChildren()[0]->getName()!="project")
8058         {
8059         error("Main xml element should be <project>");
8060         delete root;
8061         return false;
8062         }
8064     //########## Project attributes
8065     Element *project = root->getChildren()[0];
8066     String s = project->getAttribute("name");
8067     if (s.size() > 0)
8068         projectName = s;
8069     s = project->getAttribute("default");
8070     if (s.size() > 0)
8071         defaultTarget = s;
8072     s = project->getAttribute("basedir");
8073     if (s.size() > 0)
8074         baseDir = s;
8076     //######### PARSE MEMBERS
8077     std::vector<Element *> children = project->getChildren();
8078     for (unsigned int i=0 ; i<children.size() ; i++)
8079         {
8080         Element *elem = children[i];
8081         String tagName = elem->getName();
8083         //########## DESCRIPTION
8084         if (tagName == "description")
8085             {
8086             description = parser.trim(elem->getValue());
8087             }
8089         //######### PROPERTY
8090         else if (tagName == "property")
8091             {
8092             if (!parseProperty(elem))
8093                 return false;
8094             }
8096         //######### TARGET
8097         else if (tagName == "target")
8098             {
8099             String tname   = elem->getAttribute("name");
8100             String tdesc   = elem->getAttribute("description");
8101             String tdeps   = elem->getAttribute("depends");
8102             String tif     = elem->getAttribute("if");
8103             String tunless = elem->getAttribute("unless");
8104             Target target(*this);
8105             target.setName(tname);
8106             target.setDescription(tdesc);
8107             target.parseDependencies(tdeps);
8108             target.setIf(tif);
8109             target.setUnless(tunless);
8110             std::vector<Element *> telems = elem->getChildren();
8111             for (unsigned int i=0 ; i<telems.size() ; i++)
8112                 {
8113                 Element *telem = telems[i];
8114                 Task breeder(*this);
8115                 Task *task = breeder.createTask(telem);
8116                 if (!task)
8117                     return false;
8118                 allTasks.push_back(task);
8119                 target.addTask(task);
8120                 }
8122             //Check name
8123             if (tname.size() == 0)
8124                 {
8125                 error("no name for target");
8126                 return false;
8127                 }
8128             //Check for duplicate name
8129             if (targets.find(tname) != targets.end())
8130                 {
8131                 error("target '%s' already defined", tname.c_str());
8132                 return false;
8133                 }
8134             //more work than targets[tname]=target, but avoids default allocator
8135             targets.insert(std::make_pair<String, Target>(tname, target));
8136             }
8138         }
8140     std::map<String, Target>::iterator iter;
8141     for (iter = targets.begin() ; iter!= targets.end() ; iter++)
8142         {
8143         Target tgt = iter->second;
8144         std::vector<String> depList;
8145         if (!checkTargetDependencies(tgt, depList))
8146             {
8147             return false;
8148             }
8149         }
8152     delete root;
8153     status("######## PARSE COMPLETE");
8154     return true;
8158 /**
8159  * Overload a <property>
8160  */
8161 bool Make::specifyProperty(const String &name, const String &value)
8163     if (specifiedProperties.find(name) != specifiedProperties.end())
8164         {
8165         error("Property %s already specified", name.c_str());
8166         return false;
8167         }
8168     specifiedProperties[name] = value;
8169     return true;
8174 /**
8175  *
8176  */
8177 bool Make::run()
8179     if (!parseFile())
8180         return false;
8181         
8182     if (!execute())
8183         return false;
8185     return true;
8191 /**
8192  * Get a formatted MM:SS.sss time elapsed string
8193  */ 
8194 static String
8195 timeDiffString(struct timeval &x, struct timeval &y)
8197     long microsX  = x.tv_usec;
8198     long secondsX = x.tv_sec;
8199     long microsY  = y.tv_usec;
8200     long secondsY = y.tv_sec;
8201     if (microsX < microsY)
8202         {
8203         microsX += 1000000;
8204         secondsX -= 1;
8205         }
8207     int seconds = (int)(secondsX - secondsY);
8208     int millis  = (int)((microsX - microsY)/1000);
8210     int minutes = seconds/60;
8211     seconds += minutes*60;
8212     char buf[80];
8213     snprintf(buf, 79, "%dm %d.%03ds", minutes, seconds, millis);
8214     String ret = buf;
8215     return ret;
8216     
8219 /**
8220  *
8221  */
8222 bool Make::run(const String &target)
8224     status("####################################################");
8225     status("#   %s", version().c_str());
8226     status("####################################################");
8227     struct timeval timeStart, timeEnd;
8228     ::gettimeofday(&timeStart, NULL);
8229     specifiedTarget = target;
8230     if (!run())
8231         return false;
8232     ::gettimeofday(&timeEnd, NULL);
8233     String timeStr = timeDiffString(timeEnd, timeStart);
8234     status("####################################################");
8235     status("#   BuildTool Completed : %s", timeStr.c_str());
8236     status("####################################################");
8237     return true;
8246 }// namespace buildtool
8247 //########################################################################
8248 //# M A I N
8249 //########################################################################
8251 typedef buildtool::String String;
8253 /**
8254  *  Format an error message in printf() style
8255  */
8256 static void error(char *fmt, ...)
8258     va_list ap;
8259     va_start(ap, fmt);
8260     fprintf(stderr, "BuildTool error: ");
8261     vfprintf(stderr, fmt, ap);
8262     fprintf(stderr, "\n");
8263     va_end(ap);
8267 static bool parseProperty(const String &s, String &name, String &val)
8269     int len = s.size();
8270     int i;
8271     for (i=0 ; i<len ; i++)
8272         {
8273         char ch = s[i];
8274         if (ch == '=')
8275             break;
8276         name.push_back(ch);
8277         }
8278     if (i>=len || s[i]!='=')
8279         {
8280         error("property requires -Dname=value");
8281         return false;
8282         }
8283     i++;
8284     for ( ; i<len ; i++)
8285         {
8286         char ch = s[i];
8287         val.push_back(ch);
8288         }
8289     return true;
8293 /**
8294  * Compare a buffer with a key, for the length of the key
8295  */
8296 static bool sequ(const String &buf, char *key)
8298     int len = buf.size();
8299     for (int i=0 ; key[i] && i<len ; i++)
8300         {
8301         if (key[i] != buf[i])
8302             return false;
8303         }        
8304     return true;
8307 static void usage(int argc, char **argv)
8309     printf("usage:\n");
8310     printf("   %s [options] [target]\n", argv[0]);
8311     printf("Options:\n");
8312     printf("  -help, -h              print this message\n");
8313     printf("  -version               print the version information and exit\n");
8314     printf("  -file <file>           use given buildfile\n");
8315     printf("  -f <file>                 ''\n");
8316     printf("  -D<property>=<value>   use value for given property\n");
8322 /**
8323  * Parse the command-line args, get our options,
8324  * and run this thing
8325  */   
8326 static bool parseOptions(int argc, char **argv)
8328     if (argc < 1)
8329         {
8330         error("Cannot parse arguments");
8331         return false;
8332         }
8334     buildtool::Make make;
8336     String target;
8338     //char *progName = argv[0];
8339     for (int i=1 ; i<argc ; i++)
8340         {
8341         String arg = argv[i];
8342         if (arg.size()>1 && arg[0]=='-')
8343             {
8344             if (arg == "-h" || arg == "-help")
8345                 {
8346                 usage(argc,argv);
8347                 return true;
8348                 }
8349             else if (arg == "-version")
8350                 {
8351                 printf("%s", make.version().c_str());
8352                 return true;
8353                 }
8354             else if (arg == "-f" || arg == "-file")
8355                 {
8356                 if (i>=argc)
8357                    {
8358                    usage(argc, argv);
8359                    return false;
8360                    }
8361                 i++; //eat option
8362                 make.setURI(argv[i]);
8363                 }
8364             else if (arg.size()>2 && sequ(arg, "-D"))
8365                 {
8366                 String s = arg.substr(2, s.size());
8367                 String name, value;
8368                 if (!parseProperty(s, name, value))
8369                    {
8370                    usage(argc, argv);
8371                    return false;
8372                    }
8373                 if (!make.specifyProperty(name, value))
8374                     return false;
8375                 }
8376             else
8377                 {
8378                 error("Unknown option:%s", arg.c_str());
8379                 return false;
8380                 }
8381             }
8382         else
8383             {
8384             if (target.size()>0)
8385                 {
8386                 error("only one initial target");
8387                 usage(argc, argv);
8388                 return false;
8389                 }
8390             target = arg;
8391             }
8392         }
8394     //We have the options.  Now execute them
8395     if (!make.run(target))
8396         return false;
8398     return true;
8404 /*
8405 static bool runMake()
8407     buildtool::Make make;
8408     if (!make.run())
8409         return false;
8410     return true;
8414 static bool pkgConfigTest()
8416     buildtool::PkgConfig pkgConfig;
8417     if (!pkgConfig.readFile("gtk+-2.0.pc"))
8418         return false;
8419     return true;
8424 static bool depTest()
8426     buildtool::DepTool deptool;
8427     deptool.setSourceDirectory("/dev/ink/inkscape/src");
8428     if (!deptool.generateDependencies("build.dep"))
8429         return false;
8430     std::vector<buildtool::DepRec> res =
8431            deptool.loadDepFile("build.dep");
8432     if (res.size() == 0)
8433         return false;
8434     return true;
8437 static bool popenTest()
8439     buildtool::Make make;
8440     buildtool::String out, err;
8441     bool ret = make.executeCommand("gcc xx.cpp", "", out, err);
8442     printf("Popen test:%d '%s' '%s'\n", ret, out.c_str(), err.c_str());
8443     return true;
8447 static bool propFileTest()
8449     buildtool::Make make;
8450     make.parsePropertyFile("test.prop", "test.");
8451     return true;
8453 */
8455 int main(int argc, char **argv)
8458     if (!parseOptions(argc, argv))
8459         return 1;
8460     /*
8461     if (!popenTest())
8462         return 1;
8464     if (!depTest())
8465         return 1;
8466     if (!propFileTest())
8467         return 1;
8468     if (runMake())
8469         return 1;
8470     */
8471     return 0;
8475 //########################################################################
8476 //# E N D 
8477 //########################################################################