Code

Switch from the HAVE_GETTIMEOFDAY option to NEED_GETTIMEOFDAY since most people have it.
[inkscape.git] / buildtool.cpp
1 /**
2  * Simple build automation tool.
3  *
4  * Authors:
5  *   Bob Jamison
6  *
7  * Copyright (C) 2006-2007 Bob Jamison
8  *
9  *  This library is free software; you can redistribute it and/or
10  *  modify it under the terms of the GNU Lesser General Public
11  *  License as published by the Free Software Foundation; either
12  *  version 2.1 of the License, or (at your option) any later version.
13  *
14  *  This library is distributed in the hope that it will be useful,
15  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  *  Lesser General Public License for more details.
18  *
19  *  You should have received a copy of the GNU Lesser General Public
20  *  License along with this library; if not, write to the Free Software
21  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
22  */
24 /**
25  * To use this file, compile with:
26  * <pre>
27  * g++ -O3 buildtool.cpp -o btool.exe
28  * (or whatever your compiler might be) 
29  * Then
30  * btool
31  * or 
32  * btool {target}
33  * 
34  * Note: if you are using MinGW, and a not very recent version of it,
35  * gettimeofday() might be missing.  If so, just build this file with
36  * this command:
37  * g++ -O3 -DNEED_GETTIMEOFDAY buildtool.cpp -o btool.exe
38  *     
39  */  
41 #define BUILDTOOL_VERSION  "BuildTool v0.6.12, 2007 Bob Jamison"
43 #include <stdio.h>
44 #include <fcntl.h>
45 #include <unistd.h>
46 #include <stdarg.h>
47 #include <sys/stat.h>
48 #include <time.h>
49 #include <sys/time.h>
50 #include <utime.h>
51 #include <dirent.h>
53 #include <string>
54 #include <map>
55 #include <set>
56 #include <vector>
58 #ifdef __WIN32__
59 #include <windows.h>
60 #endif
63 #include <errno.h>
66 //########################################################################
67 //# Definition of gettimeofday() for those who don't have it
68 //########################################################################
69 #ifdef NEED_GETTIMEOFDAY
70 #include <sys/timeb.h>
72 struct timezone {
73       int tz_minuteswest; /* minutes west of Greenwich */
74       int tz_dsttime;     /* type of dst correction */
75     };
77 static int gettimeofday (struct timeval *tv, struct timezone *tz)
78 {
79    struct _timeb tb;
81    if (!tv)
82       return (-1);
84     _ftime (&tb);
85     tv->tv_sec  = tb.time;
86     tv->tv_usec = tb.millitm * 1000 + 500;
87     if (tz)
88         {
89         tz->tz_minuteswest = -60 * _timezone;
90         tz->tz_dsttime = _daylight;
91         }
92     return 0;
93 }
95 #endif
103 namespace buildtool
109 //########################################################################
110 //########################################################################
111 //##  R E G E X P
112 //########################################################################
113 //########################################################################
115 /**
116  * This is the T-Rex regular expression library, which we
117  * gratefully acknowledge.  It's clean code and small size allow
118  * us to embed it in BuildTool without adding a dependency
119  *
120  */    
122 //begin trex.h
124 #ifndef _TREX_H_
125 #define _TREX_H_
126 /***************************************************************
127     T-Rex a tiny regular expression library
129     Copyright (C) 2003-2006 Alberto Demichelis
131     This software is provided 'as-is', without any express 
132     or implied warranty. In no event will the authors be held 
133     liable for any damages arising from the use of this software.
135     Permission is granted to anyone to use this software for 
136     any purpose, including commercial applications, and to alter
137     it and redistribute it freely, subject to the following restrictions:
139         1. The origin of this software must not be misrepresented;
140         you must not claim that you wrote the original software.
141         If you use this software in a product, an acknowledgment
142         in the product documentation would be appreciated but
143         is not required.
145         2. Altered source versions must be plainly marked as such,
146         and must not be misrepresented as being the original software.
148         3. This notice may not be removed or altered from any
149         source distribution.
151 ****************************************************************/
153 #ifdef _UNICODE
154 #define TRexChar unsigned short
155 #define MAX_CHAR 0xFFFF
156 #define _TREXC(c) L##c 
157 #define trex_strlen wcslen
158 #define trex_printf wprintf
159 #else
160 #define TRexChar char
161 #define MAX_CHAR 0xFF
162 #define _TREXC(c) (c) 
163 #define trex_strlen strlen
164 #define trex_printf printf
165 #endif
167 #ifndef TREX_API
168 #define TREX_API extern
169 #endif
171 #define TRex_True 1
172 #define TRex_False 0
174 typedef unsigned int TRexBool;
175 typedef struct TRex TRex;
177 typedef struct {
178     const TRexChar *begin;
179     int len;
180 } TRexMatch;
182 TREX_API TRex *trex_compile(const TRexChar *pattern,const TRexChar **error);
183 TREX_API void trex_free(TRex *exp);
184 TREX_API TRexBool trex_match(TRex* exp,const TRexChar* text);
185 TREX_API TRexBool trex_search(TRex* exp,const TRexChar* text, const TRexChar** out_begin, const TRexChar** out_end);
186 TREX_API TRexBool trex_searchrange(TRex* exp,const TRexChar* text_begin,const TRexChar* text_end,const TRexChar** out_begin, const TRexChar** out_end);
187 TREX_API int trex_getsubexpcount(TRex* exp);
188 TREX_API TRexBool trex_getsubexp(TRex* exp, int n, TRexMatch *subexp);
190 #endif
192 //end trex.h
194 //start trex.c
197 #include <stdio.h>
198 #include <string>
200 /* see copyright notice in trex.h */
201 #include <string.h>
202 #include <stdlib.h>
203 #include <ctype.h>
204 #include <setjmp.h>
205 //#include "trex.h"
207 #ifdef _UINCODE
208 #define scisprint iswprint
209 #define scstrlen wcslen
210 #define scprintf wprintf
211 #define _SC(x) L(x)
212 #else
213 #define scisprint isprint
214 #define scstrlen strlen
215 #define scprintf printf
216 #define _SC(x) (x)
217 #endif
219 #ifdef _DEBUG
220 #include <stdio.h>
222 static const TRexChar *g_nnames[] =
224     _SC("NONE"),_SC("OP_GREEDY"),    _SC("OP_OR"),
225     _SC("OP_EXPR"),_SC("OP_NOCAPEXPR"),_SC("OP_DOT"),    _SC("OP_CLASS"),
226     _SC("OP_CCLASS"),_SC("OP_NCLASS"),_SC("OP_RANGE"),_SC("OP_CHAR"),
227     _SC("OP_EOL"),_SC("OP_BOL"),_SC("OP_WB")
228 };
230 #endif
231 #define OP_GREEDY        (MAX_CHAR+1) // * + ? {n}
232 #define OP_OR            (MAX_CHAR+2)
233 #define OP_EXPR            (MAX_CHAR+3) //parentesis ()
234 #define OP_NOCAPEXPR    (MAX_CHAR+4) //parentesis (?:)
235 #define OP_DOT            (MAX_CHAR+5)
236 #define OP_CLASS        (MAX_CHAR+6)
237 #define OP_CCLASS        (MAX_CHAR+7)
238 #define OP_NCLASS        (MAX_CHAR+8) //negates class the [^
239 #define OP_RANGE        (MAX_CHAR+9)
240 #define OP_CHAR            (MAX_CHAR+10)
241 #define OP_EOL            (MAX_CHAR+11)
242 #define OP_BOL            (MAX_CHAR+12)
243 #define OP_WB            (MAX_CHAR+13)
245 #define TREX_SYMBOL_ANY_CHAR ('.')
246 #define TREX_SYMBOL_GREEDY_ONE_OR_MORE ('+')
247 #define TREX_SYMBOL_GREEDY_ZERO_OR_MORE ('*')
248 #define TREX_SYMBOL_GREEDY_ZERO_OR_ONE ('?')
249 #define TREX_SYMBOL_BRANCH ('|')
250 #define TREX_SYMBOL_END_OF_STRING ('$')
251 #define TREX_SYMBOL_BEGINNING_OF_STRING ('^')
252 #define TREX_SYMBOL_ESCAPE_CHAR ('\\')
255 typedef int TRexNodeType;
257 typedef struct tagTRexNode{
258     TRexNodeType type;
259     int left;
260     int right;
261     int next;
262 }TRexNode;
264 struct TRex{
265     const TRexChar *_eol;
266     const TRexChar *_bol;
267     const TRexChar *_p;
268     int _first;
269     int _op;
270     TRexNode *_nodes;
271     int _nallocated;
272     int _nsize;
273     int _nsubexpr;
274     TRexMatch *_matches;
275     int _currsubexp;
276     void *_jmpbuf;
277     const TRexChar **_error;
278 };
280 static int trex_list(TRex *exp);
282 static int trex_newnode(TRex *exp, TRexNodeType type)
284     TRexNode n;
285     int newid;
286     n.type = type;
287     n.next = n.right = n.left = -1;
288     if(type == OP_EXPR)
289         n.right = exp->_nsubexpr++;
290     if(exp->_nallocated < (exp->_nsize + 1)) {
291         //int oldsize = exp->_nallocated;
292         exp->_nallocated *= 2;
293         exp->_nodes = (TRexNode *)realloc(exp->_nodes, exp->_nallocated * sizeof(TRexNode));
294     }
295     exp->_nodes[exp->_nsize++] = n;
296     newid = exp->_nsize - 1;
297     return (int)newid;
300 static void trex_error(TRex *exp,const TRexChar *error)
302     if(exp->_error) *exp->_error = error;
303     longjmp(*((jmp_buf*)exp->_jmpbuf),-1);
306 static void trex_expect(TRex *exp, int n){
307     if((*exp->_p) != n) 
308         trex_error(exp, _SC("expected paren"));
309     exp->_p++;
312 static TRexChar trex_escapechar(TRex *exp)
314     if(*exp->_p == TREX_SYMBOL_ESCAPE_CHAR){
315         exp->_p++;
316         switch(*exp->_p) {
317         case 'v': exp->_p++; return '\v';
318         case 'n': exp->_p++; return '\n';
319         case 't': exp->_p++; return '\t';
320         case 'r': exp->_p++; return '\r';
321         case 'f': exp->_p++; return '\f';
322         default: return (*exp->_p++);
323         }
324     } else if(!scisprint(*exp->_p)) trex_error(exp,_SC("letter expected"));
325     return (*exp->_p++);
328 static int trex_charclass(TRex *exp,int classid)
330     int n = trex_newnode(exp,OP_CCLASS);
331     exp->_nodes[n].left = classid;
332     return n;
335 static int trex_charnode(TRex *exp,TRexBool isclass)
337     TRexChar t;
338     if(*exp->_p == TREX_SYMBOL_ESCAPE_CHAR) {
339         exp->_p++;
340         switch(*exp->_p) {
341             case 'n': exp->_p++; return trex_newnode(exp,'\n');
342             case 't': exp->_p++; return trex_newnode(exp,'\t');
343             case 'r': exp->_p++; return trex_newnode(exp,'\r');
344             case 'f': exp->_p++; return trex_newnode(exp,'\f');
345             case 'v': exp->_p++; return trex_newnode(exp,'\v');
346             case 'a': case 'A': case 'w': case 'W': case 's': case 'S': 
347             case 'd': case 'D': case 'x': case 'X': case 'c': case 'C': 
348             case 'p': case 'P': case 'l': case 'u': 
349                 {
350                 t = *exp->_p; exp->_p++; 
351                 return trex_charclass(exp,t);
352                 }
353             case 'b': 
354             case 'B':
355                 if(!isclass) {
356                     int node = trex_newnode(exp,OP_WB);
357                     exp->_nodes[node].left = *exp->_p;
358                     exp->_p++; 
359                     return node;
360                 } //else default
361             default: 
362                 t = *exp->_p; exp->_p++; 
363                 return trex_newnode(exp,t);
364         }
365     }
366     else if(!scisprint(*exp->_p)) {
367         
368         trex_error(exp,_SC("letter expected"));
369     }
370     t = *exp->_p; exp->_p++; 
371     return trex_newnode(exp,t);
373 static int trex_class(TRex *exp)
375     int ret = -1;
376     int first = -1,chain;
377     if(*exp->_p == TREX_SYMBOL_BEGINNING_OF_STRING){
378         ret = trex_newnode(exp,OP_NCLASS);
379         exp->_p++;
380     }else ret = trex_newnode(exp,OP_CLASS);
381     
382     if(*exp->_p == ']') trex_error(exp,_SC("empty class"));
383     chain = ret;
384     while(*exp->_p != ']' && exp->_p != exp->_eol) {
385         if(*exp->_p == '-' && first != -1){ 
386             int r,t;
387             if(*exp->_p++ == ']') trex_error(exp,_SC("unfinished range"));
388             r = trex_newnode(exp,OP_RANGE);
389             if(first>*exp->_p) trex_error(exp,_SC("invalid range"));
390             if(exp->_nodes[first].type == OP_CCLASS) trex_error(exp,_SC("cannot use character classes in ranges"));
391             exp->_nodes[r].left = exp->_nodes[first].type;
392             t = trex_escapechar(exp);
393             exp->_nodes[r].right = t;
394             exp->_nodes[chain].next = r;
395             chain = r;
396             first = -1;
397         }
398         else{
399             if(first!=-1){
400                 int c = first;
401                 exp->_nodes[chain].next = c;
402                 chain = c;
403                 first = trex_charnode(exp,TRex_True);
404             }
405             else{
406                 first = trex_charnode(exp,TRex_True);
407             }
408         }
409     }
410     if(first!=-1){
411         int c = first;
412         exp->_nodes[chain].next = c;
413         chain = c;
414         first = -1;
415     }
416     /* hack? */
417     exp->_nodes[ret].left = exp->_nodes[ret].next;
418     exp->_nodes[ret].next = -1;
419     return ret;
422 static int trex_parsenumber(TRex *exp)
424     int ret = *exp->_p-'0';
425     int positions = 10;
426     exp->_p++;
427     while(isdigit(*exp->_p)) {
428         ret = ret*10+(*exp->_p++-'0');
429         if(positions==1000000000) trex_error(exp,_SC("overflow in numeric constant"));
430         positions *= 10;
431     };
432     return ret;
435 static int trex_element(TRex *exp)
437     int ret = -1;
438     switch(*exp->_p)
439     {
440     case '(': {
441         int expr,newn;
442         exp->_p++;
445         if(*exp->_p =='?') {
446             exp->_p++;
447             trex_expect(exp,':');
448             expr = trex_newnode(exp,OP_NOCAPEXPR);
449         }
450         else
451             expr = trex_newnode(exp,OP_EXPR);
452         newn = trex_list(exp);
453         exp->_nodes[expr].left = newn;
454         ret = expr;
455         trex_expect(exp,')');
456               }
457               break;
458     case '[':
459         exp->_p++;
460         ret = trex_class(exp);
461         trex_expect(exp,']');
462         break;
463     case TREX_SYMBOL_END_OF_STRING: exp->_p++; ret = trex_newnode(exp,OP_EOL);break;
464     case TREX_SYMBOL_ANY_CHAR: exp->_p++; ret = trex_newnode(exp,OP_DOT);break;
465     default:
466         ret = trex_charnode(exp,TRex_False);
467         break;
468     }
470     {
471         int op;
472         TRexBool isgreedy = TRex_False;
473         unsigned short p0 = 0, p1 = 0;
474         switch(*exp->_p){
475             case TREX_SYMBOL_GREEDY_ZERO_OR_MORE: p0 = 0; p1 = 0xFFFF; exp->_p++; isgreedy = TRex_True; break;
476             case TREX_SYMBOL_GREEDY_ONE_OR_MORE: p0 = 1; p1 = 0xFFFF; exp->_p++; isgreedy = TRex_True; break;
477             case TREX_SYMBOL_GREEDY_ZERO_OR_ONE: p0 = 0; p1 = 1; exp->_p++; isgreedy = TRex_True; break;
478             case '{':
479                 exp->_p++;
480                 if(!isdigit(*exp->_p)) trex_error(exp,_SC("number expected"));
481                 p0 = (unsigned short)trex_parsenumber(exp);
482                 /*******************************/
483                 switch(*exp->_p) {
484             case '}':
485                 p1 = p0; exp->_p++;
486                 break;
487             case ',':
488                 exp->_p++;
489                 p1 = 0xFFFF;
490                 if(isdigit(*exp->_p)){
491                     p1 = (unsigned short)trex_parsenumber(exp);
492                 }
493                 trex_expect(exp,'}');
494                 break;
495             default:
496                 trex_error(exp,_SC(", or } expected"));
497         }
498         /*******************************/
499         isgreedy = TRex_True; 
500         break;
502         }
503         if(isgreedy) {
504             int nnode = trex_newnode(exp,OP_GREEDY);
505             op = OP_GREEDY;
506             exp->_nodes[nnode].left = ret;
507             exp->_nodes[nnode].right = ((p0)<<16)|p1;
508             ret = nnode;
509         }
510     }
511     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')) {
512         int nnode = trex_element(exp);
513         exp->_nodes[ret].next = nnode;
514     }
516     return ret;
519 static int trex_list(TRex *exp)
521     int ret=-1,e;
522     if(*exp->_p == TREX_SYMBOL_BEGINNING_OF_STRING) {
523         exp->_p++;
524         ret = trex_newnode(exp,OP_BOL);
525     }
526     e = trex_element(exp);
527     if(ret != -1) {
528         exp->_nodes[ret].next = e;
529     }
530     else ret = e;
532     if(*exp->_p == TREX_SYMBOL_BRANCH) {
533         int temp,tright;
534         exp->_p++;
535         temp = trex_newnode(exp,OP_OR);
536         exp->_nodes[temp].left = ret;
537         tright = trex_list(exp);
538         exp->_nodes[temp].right = tright;
539         ret = temp;
540     }
541     return ret;
544 static TRexBool trex_matchcclass(int cclass,TRexChar c)
546     switch(cclass) {
547     case 'a': return isalpha(c)?TRex_True:TRex_False;
548     case 'A': return !isalpha(c)?TRex_True:TRex_False;
549     case 'w': return (isalnum(c) || c == '_')?TRex_True:TRex_False;
550     case 'W': return (!isalnum(c) && c != '_')?TRex_True:TRex_False;
551     case 's': return isspace(c)?TRex_True:TRex_False;
552     case 'S': return !isspace(c)?TRex_True:TRex_False;
553     case 'd': return isdigit(c)?TRex_True:TRex_False;
554     case 'D': return !isdigit(c)?TRex_True:TRex_False;
555     case 'x': return isxdigit(c)?TRex_True:TRex_False;
556     case 'X': return !isxdigit(c)?TRex_True:TRex_False;
557     case 'c': return iscntrl(c)?TRex_True:TRex_False;
558     case 'C': return !iscntrl(c)?TRex_True:TRex_False;
559     case 'p': return ispunct(c)?TRex_True:TRex_False;
560     case 'P': return !ispunct(c)?TRex_True:TRex_False;
561     case 'l': return islower(c)?TRex_True:TRex_False;
562     case 'u': return isupper(c)?TRex_True:TRex_False;
563     }
564     return TRex_False; /*cannot happen*/
567 static TRexBool trex_matchclass(TRex* exp,TRexNode *node,TRexChar c)
569     do {
570         switch(node->type) {
571             case OP_RANGE:
572                 if(c >= node->left && c <= node->right) return TRex_True;
573                 break;
574             case OP_CCLASS:
575                 if(trex_matchcclass(node->left,c)) return TRex_True;
576                 break;
577             default:
578                 if(c == node->type)return TRex_True;
579         }
580     } while((node->next != -1) && (node = &exp->_nodes[node->next]));
581     return TRex_False;
584 static const TRexChar *trex_matchnode(TRex* exp,TRexNode *node,const TRexChar *str,TRexNode *next)
586     
587     TRexNodeType type = node->type;
588     switch(type) {
589     case OP_GREEDY: {
590         //TRexNode *greedystop = (node->next != -1) ? &exp->_nodes[node->next] : NULL;
591         TRexNode *greedystop = NULL;
592         int p0 = (node->right >> 16)&0x0000FFFF, p1 = node->right&0x0000FFFF, nmaches = 0;
593         const TRexChar *s=str, *good = str;
595         if(node->next != -1) {
596             greedystop = &exp->_nodes[node->next];
597         }
598         else {
599             greedystop = next;
600         }
602         while((nmaches == 0xFFFF || nmaches < p1)) {
604             const TRexChar *stop;
605             if(!(s = trex_matchnode(exp,&exp->_nodes[node->left],s,greedystop)))
606                 break;
607             nmaches++;
608             good=s;
609             if(greedystop) {
610                 //checks that 0 matches satisfy the expression(if so skips)
611                 //if not would always stop(for instance if is a '?')
612                 if(greedystop->type != OP_GREEDY ||
613                 (greedystop->type == OP_GREEDY && ((greedystop->right >> 16)&0x0000FFFF) != 0))
614                 {
615                     TRexNode *gnext = NULL;
616                     if(greedystop->next != -1) {
617                         gnext = &exp->_nodes[greedystop->next];
618                     }else if(next && next->next != -1){
619                         gnext = &exp->_nodes[next->next];
620                     }
621                     stop = trex_matchnode(exp,greedystop,s,gnext);
622                     if(stop) {
623                         //if satisfied stop it
624                         if(p0 == p1 && p0 == nmaches) break;
625                         else if(nmaches >= p0 && p1 == 0xFFFF) break;
626                         else if(nmaches >= p0 && nmaches <= p1) break;
627                     }
628                 }
629             }
630             
631             if(s >= exp->_eol)
632                 break;
633         }
634         if(p0 == p1 && p0 == nmaches) return good;
635         else if(nmaches >= p0 && p1 == 0xFFFF) return good;
636         else if(nmaches >= p0 && nmaches <= p1) return good;
637         return NULL;
638     }
639     case OP_OR: {
640             const TRexChar *asd = str;
641             TRexNode *temp=&exp->_nodes[node->left];
642             while( (asd = trex_matchnode(exp,temp,asd,NULL)) ) {
643                 if(temp->next != -1)
644                     temp = &exp->_nodes[temp->next];
645                 else
646                     return asd;
647             }
648             asd = str;
649             temp = &exp->_nodes[node->right];
650             while( (asd = trex_matchnode(exp,temp,asd,NULL)) ) {
651                 if(temp->next != -1)
652                     temp = &exp->_nodes[temp->next];
653                 else
654                     return asd;
655             }
656             return NULL;
657             break;
658     }
659     case OP_EXPR:
660     case OP_NOCAPEXPR:{
661             TRexNode *n = &exp->_nodes[node->left];
662             const TRexChar *cur = str;
663             int capture = -1;
664             if(node->type != OP_NOCAPEXPR && node->right == exp->_currsubexp) {
665                 capture = exp->_currsubexp;
666                 exp->_matches[capture].begin = cur;
667                 exp->_currsubexp++;
668             }
669             
670             do {
671                 TRexNode *subnext = NULL;
672                 if(n->next != -1) {
673                     subnext = &exp->_nodes[n->next];
674                 }else {
675                     subnext = next;
676                 }
677                 if(!(cur = trex_matchnode(exp,n,cur,subnext))) {
678                     if(capture != -1){
679                         exp->_matches[capture].begin = 0;
680                         exp->_matches[capture].len = 0;
681                     }
682                     return NULL;
683                 }
684             } while((n->next != -1) && (n = &exp->_nodes[n->next]));
686             if(capture != -1) 
687                 exp->_matches[capture].len = cur - exp->_matches[capture].begin;
688             return cur;
689     }                 
690     case OP_WB:
691         if(str == exp->_bol && !isspace(*str)
692          || (str == exp->_eol && !isspace(*(str-1)))
693          || (!isspace(*str) && isspace(*(str+1)))
694          || (isspace(*str) && !isspace(*(str+1))) ) {
695             return (node->left == 'b')?str:NULL;
696         }
697         return (node->left == 'b')?NULL:str;
698     case OP_BOL:
699         if(str == exp->_bol) return str;
700         return NULL;
701     case OP_EOL:
702         if(str == exp->_eol) return str;
703         return NULL;
704     case OP_DOT:{
705         *str++;
706                 }
707         return str;
708     case OP_NCLASS:
709     case OP_CLASS:
710         if(trex_matchclass(exp,&exp->_nodes[node->left],*str)?(type == OP_CLASS?TRex_True:TRex_False):(type == OP_NCLASS?TRex_True:TRex_False)) {
711             *str++;
712             return str;
713         }
714         return NULL;
715     case OP_CCLASS:
716         if(trex_matchcclass(node->left,*str)) {
717             *str++;
718             return str;
719         }
720         return NULL;
721     default: /* char */
722         if(*str != node->type) return NULL;
723         *str++;
724         return str;
725     }
726     return NULL;
729 /* public api */
730 TRex *trex_compile(const TRexChar *pattern,const TRexChar **error)
732     TRex *exp = (TRex *)malloc(sizeof(TRex));
733     exp->_eol = exp->_bol = NULL;
734     exp->_p = pattern;
735     exp->_nallocated = (int)scstrlen(pattern) * sizeof(TRexChar);
736     exp->_nodes = (TRexNode *)malloc(exp->_nallocated * sizeof(TRexNode));
737     exp->_nsize = 0;
738     exp->_matches = 0;
739     exp->_nsubexpr = 0;
740     exp->_first = trex_newnode(exp,OP_EXPR);
741     exp->_error = error;
742     exp->_jmpbuf = malloc(sizeof(jmp_buf));
743     if(setjmp(*((jmp_buf*)exp->_jmpbuf)) == 0) {
744         int res = trex_list(exp);
745         exp->_nodes[exp->_first].left = res;
746         if(*exp->_p!='\0')
747             trex_error(exp,_SC("unexpected character"));
748 #ifdef _DEBUG
749         {
750             int nsize,i;
751             TRexNode *t;
752             nsize = exp->_nsize;
753             t = &exp->_nodes[0];
754             scprintf(_SC("\n"));
755             for(i = 0;i < nsize; i++) {
756                 if(exp->_nodes[i].type>MAX_CHAR)
757                     scprintf(_SC("[%02d] %10s "),i,g_nnames[exp->_nodes[i].type-MAX_CHAR]);
758                 else
759                     scprintf(_SC("[%02d] %10c "),i,exp->_nodes[i].type);
760                 scprintf(_SC("left %02d right %02d next %02d\n"),exp->_nodes[i].left,exp->_nodes[i].right,exp->_nodes[i].next);
761             }
762             scprintf(_SC("\n"));
763         }
764 #endif
765         exp->_matches = (TRexMatch *) malloc(exp->_nsubexpr * sizeof(TRexMatch));
766         memset(exp->_matches,0,exp->_nsubexpr * sizeof(TRexMatch));
767     }
768     else{
769         trex_free(exp);
770         return NULL;
771     }
772     return exp;
775 void trex_free(TRex *exp)
777     if(exp)    {
778         if(exp->_nodes) free(exp->_nodes);
779         if(exp->_jmpbuf) free(exp->_jmpbuf);
780         if(exp->_matches) free(exp->_matches);
781         free(exp);
782     }
785 TRexBool trex_match(TRex* exp,const TRexChar* text)
787     const TRexChar* res = NULL;
788     exp->_bol = text;
789     exp->_eol = text + scstrlen(text);
790     exp->_currsubexp = 0;
791     res = trex_matchnode(exp,exp->_nodes,text,NULL);
792     if(res == NULL || res != exp->_eol)
793         return TRex_False;
794     return TRex_True;
797 TRexBool trex_searchrange(TRex* exp,const TRexChar* text_begin,const TRexChar* text_end,const TRexChar** out_begin, const TRexChar** out_end)
799     const TRexChar *cur = NULL;
800     int node = exp->_first;
801     if(text_begin >= text_end) return TRex_False;
802     exp->_bol = text_begin;
803     exp->_eol = text_end;
804     do {
805         cur = text_begin;
806         while(node != -1) {
807             exp->_currsubexp = 0;
808             cur = trex_matchnode(exp,&exp->_nodes[node],cur,NULL);
809             if(!cur)
810                 break;
811             node = exp->_nodes[node].next;
812         }
813         *text_begin++;
814     } while(cur == NULL && text_begin != text_end);
816     if(cur == NULL)
817         return TRex_False;
819     --text_begin;
821     if(out_begin) *out_begin = text_begin;
822     if(out_end) *out_end = cur;
823     return TRex_True;
826 TRexBool trex_search(TRex* exp,const TRexChar* text, const TRexChar** out_begin, const TRexChar** out_end)
828     return trex_searchrange(exp,text,text + scstrlen(text),out_begin,out_end);
831 int trex_getsubexpcount(TRex* exp)
833     return exp->_nsubexpr;
836 TRexBool trex_getsubexp(TRex* exp, int n, TRexMatch *subexp)
838     if( n<0 || n >= exp->_nsubexpr) return TRex_False;
839     *subexp = exp->_matches[n];
840     return TRex_True;
844 //########################################################################
845 //########################################################################
846 //##  E N D    R E G E X P
847 //########################################################################
848 //########################################################################
854 //########################################################################
855 //########################################################################
856 //##  X M L
857 //########################################################################
858 //########################################################################
860 // Note:  This mini-dom library comes from Pedro, another little project
861 // of mine.
863 typedef std::string String;
864 typedef unsigned int XMLCh;
867 class Namespace
869 public:
870     Namespace()
871         {}
873     Namespace(const String &prefixArg, const String &namespaceURIArg)
874         {
875         prefix       = prefixArg;
876         namespaceURI = namespaceURIArg;
877         }
879     Namespace(const Namespace &other)
880         {
881         assign(other);
882         }
884     Namespace &operator=(const Namespace &other)
885         {
886         assign(other);
887         return *this;
888         }
890     virtual ~Namespace()
891         {}
893     virtual String getPrefix()
894         { return prefix; }
896     virtual String getNamespaceURI()
897         { return namespaceURI; }
899 protected:
901     void assign(const Namespace &other)
902         {
903         prefix       = other.prefix;
904         namespaceURI = other.namespaceURI;
905         }
907     String prefix;
908     String namespaceURI;
910 };
912 class Attribute
914 public:
915     Attribute()
916         {}
918     Attribute(const String &nameArg, const String &valueArg)
919         {
920         name  = nameArg;
921         value = valueArg;
922         }
924     Attribute(const Attribute &other)
925         {
926         assign(other);
927         }
929     Attribute &operator=(const Attribute &other)
930         {
931         assign(other);
932         return *this;
933         }
935     virtual ~Attribute()
936         {}
938     virtual String getName()
939         { return name; }
941     virtual String getValue()
942         { return value; }
944 protected:
946     void assign(const Attribute &other)
947         {
948         name  = other.name;
949         value = other.value;
950         }
952     String name;
953     String value;
955 };
958 class Element
960 friend class Parser;
962 public:
963     Element()
964         {
965         init();
966         }
968     Element(const String &nameArg)
969         {
970         init();
971         name   = nameArg;
972         }
974     Element(const String &nameArg, const String &valueArg)
975         {
976         init();
977         name   = nameArg;
978         value  = valueArg;
979         }
981     Element(const Element &other)
982         {
983         assign(other);
984         }
986     Element &operator=(const Element &other)
987         {
988         assign(other);
989         return *this;
990         }
992     virtual Element *clone();
994     virtual ~Element()
995         {
996         for (unsigned int i=0 ; i<children.size() ; i++)
997             delete children[i];
998         }
1000     virtual String getName()
1001         { return name; }
1003     virtual String getValue()
1004         { return value; }
1006     Element *getParent()
1007         { return parent; }
1009     std::vector<Element *> getChildren()
1010         { return children; }
1012     std::vector<Element *> findElements(const String &name);
1014     String getAttribute(const String &name);
1016     std::vector<Attribute> &getAttributes()
1017         { return attributes; } 
1019     String getTagAttribute(const String &tagName, const String &attrName);
1021     String getTagValue(const String &tagName);
1023     void addChild(Element *child);
1025     void addAttribute(const String &name, const String &value);
1027     void addNamespace(const String &prefix, const String &namespaceURI);
1030     /**
1031      * Prettyprint an XML tree to an output stream.  Elements are indented
1032      * according to element hierarchy.
1033      * @param f a stream to receive the output
1034      * @param elem the element to output
1035      */
1036     void writeIndented(FILE *f);
1038     /**
1039      * Prettyprint an XML tree to standard output.  This is the equivalent of
1040      * writeIndented(stdout).
1041      * @param elem the element to output
1042      */
1043     void print();
1044     
1045     int getLine()
1046         { return line; }
1048 protected:
1050     void init()
1051         {
1052         parent = NULL;
1053         line   = 0;
1054         }
1056     void assign(const Element &other)
1057         {
1058         parent     = other.parent;
1059         children   = other.children;
1060         attributes = other.attributes;
1061         namespaces = other.namespaces;
1062         name       = other.name;
1063         value      = other.value;
1064         line       = other.line;
1065         }
1067     void findElementsRecursive(std::vector<Element *>&res, const String &name);
1069     void writeIndentedRecursive(FILE *f, int indent);
1071     Element *parent;
1073     std::vector<Element *>children;
1075     std::vector<Attribute> attributes;
1076     std::vector<Namespace> namespaces;
1078     String name;
1079     String value;
1080     
1081     int line;
1082 };
1088 class Parser
1090 public:
1091     /**
1092      * Constructor
1093      */
1094     Parser()
1095         { init(); }
1097     virtual ~Parser()
1098         {}
1100     /**
1101      * Parse XML in a char buffer.
1102      * @param buf a character buffer to parse
1103      * @param pos position to start parsing
1104      * @param len number of chars, from pos, to parse.
1105      * @return a pointer to the root of the XML document;
1106      */
1107     Element *parse(const char *buf,int pos,int len);
1109     /**
1110      * Parse XML in a char buffer.
1111      * @param buf a character buffer to parse
1112      * @param pos position to start parsing
1113      * @param len number of chars, from pos, to parse.
1114      * @return a pointer to the root of the XML document;
1115      */
1116     Element *parse(const String &buf);
1118     /**
1119      * Parse a named XML file.  The file is loaded like a data file;
1120      * the original format is not preserved.
1121      * @param fileName the name of the file to read
1122      * @return a pointer to the root of the XML document;
1123      */
1124     Element *parseFile(const String &fileName);
1126     /**
1127      * Utility method to preprocess a string for XML
1128      * output, escaping its entities.
1129      * @param str the string to encode
1130      */
1131     static String encode(const String &str);
1133     /**
1134      *  Removes whitespace from beginning and end of a string
1135      */
1136     String trim(const String &s);
1138 private:
1140     void init()
1141         {
1142         keepGoing       = true;
1143         currentNode     = NULL;
1144         parselen        = 0;
1145         parsebuf        = NULL;
1146         currentPosition = 0;
1147         }
1149     int countLines(int begin, int end);
1151     void getLineAndColumn(int pos, int *lineNr, int *colNr);
1153     void error(char *fmt, ...);
1155     int peek(int pos);
1157     int match(int pos, const char *text);
1159     int skipwhite(int p);
1161     int getWord(int p0, String &buf);
1163     int getQuoted(int p0, String &buf, int do_i_parse);
1165     int parseVersion(int p0);
1167     int parseDoctype(int p0);
1169     int parseElement(int p0, Element *par,int depth);
1171     Element *parse(XMLCh *buf,int pos,int len);
1173     bool       keepGoing;
1174     Element    *currentNode;
1175     int        parselen;
1176     XMLCh      *parsebuf;
1177     String     cdatabuf;
1178     int        currentPosition;
1179 };
1184 //########################################################################
1185 //# E L E M E N T
1186 //########################################################################
1188 Element *Element::clone()
1190     Element *elem = new Element(name, value);
1191     elem->parent     = parent;
1192     elem->attributes = attributes;
1193     elem->namespaces = namespaces;
1194     elem->line       = line;
1196     std::vector<Element *>::iterator iter;
1197     for (iter = children.begin(); iter != children.end() ; iter++)
1198         {
1199         elem->addChild((*iter)->clone());
1200         }
1201     return elem;
1205 void Element::findElementsRecursive(std::vector<Element *>&res, const String &name)
1207     if (getName() == name)
1208         {
1209         res.push_back(this);
1210         }
1211     for (unsigned int i=0; i<children.size() ; i++)
1212         children[i]->findElementsRecursive(res, name);
1215 std::vector<Element *> Element::findElements(const String &name)
1217     std::vector<Element *> res;
1218     findElementsRecursive(res, name);
1219     return res;
1222 String Element::getAttribute(const String &name)
1224     for (unsigned int i=0 ; i<attributes.size() ; i++)
1225         if (attributes[i].getName() ==name)
1226             return attributes[i].getValue();
1227     return "";
1230 String Element::getTagAttribute(const String &tagName, const String &attrName)
1232     std::vector<Element *>elems = findElements(tagName);
1233     if (elems.size() <1)
1234         return "";
1235     String res = elems[0]->getAttribute(attrName);
1236     return res;
1239 String Element::getTagValue(const String &tagName)
1241     std::vector<Element *>elems = findElements(tagName);
1242     if (elems.size() <1)
1243         return "";
1244     String res = elems[0]->getValue();
1245     return res;
1248 void Element::addChild(Element *child)
1250     if (!child)
1251         return;
1252     child->parent = this;
1253     children.push_back(child);
1257 void Element::addAttribute(const String &name, const String &value)
1259     Attribute attr(name, value);
1260     attributes.push_back(attr);
1263 void Element::addNamespace(const String &prefix, const String &namespaceURI)
1265     Namespace ns(prefix, namespaceURI);
1266     namespaces.push_back(ns);
1269 void Element::writeIndentedRecursive(FILE *f, int indent)
1271     int i;
1272     if (!f)
1273         return;
1274     //Opening tag, and attributes
1275     for (i=0;i<indent;i++)
1276         fputc(' ',f);
1277     fprintf(f,"<%s",name.c_str());
1278     for (unsigned int i=0 ; i<attributes.size() ; i++)
1279         {
1280         fprintf(f," %s=\"%s\"",
1281               attributes[i].getName().c_str(),
1282               attributes[i].getValue().c_str());
1283         }
1284     for (unsigned int i=0 ; i<namespaces.size() ; i++)
1285         {
1286         fprintf(f," xmlns:%s=\"%s\"",
1287               namespaces[i].getPrefix().c_str(),
1288               namespaces[i].getNamespaceURI().c_str());
1289         }
1290     fprintf(f,">\n");
1292     //Between the tags
1293     if (value.size() > 0)
1294         {
1295         for (int i=0;i<indent;i++)
1296             fputc(' ', f);
1297         fprintf(f," %s\n", value.c_str());
1298         }
1300     for (unsigned int i=0 ; i<children.size() ; i++)
1301         children[i]->writeIndentedRecursive(f, indent+2);
1303     //Closing tag
1304     for (int i=0; i<indent; i++)
1305         fputc(' ',f);
1306     fprintf(f,"</%s>\n", name.c_str());
1309 void Element::writeIndented(FILE *f)
1311     writeIndentedRecursive(f, 0);
1314 void Element::print()
1316     writeIndented(stdout);
1320 //########################################################################
1321 //# P A R S E R
1322 //########################################################################
1326 typedef struct
1327     {
1328     char *escaped;
1329     char value;
1330     } EntityEntry;
1332 static EntityEntry entities[] =
1334     { "&amp;" , '&'  },
1335     { "&lt;"  , '<'  },
1336     { "&gt;"  , '>'  },
1337     { "&apos;", '\'' },
1338     { "&quot;", '"'  },
1339     { NULL    , '\0' }
1340 };
1344 /**
1345  *  Removes whitespace from beginning and end of a string
1346  */
1347 String Parser::trim(const String &s)
1349     if (s.size() < 1)
1350         return s;
1351     
1352     //Find first non-ws char
1353     unsigned int begin = 0;
1354     for ( ; begin < s.size() ; begin++)
1355         {
1356         if (!isspace(s[begin]))
1357             break;
1358         }
1360     //Find first non-ws char, going in reverse
1361     unsigned int end = s.size() - 1;
1362     for ( ; end > begin ; end--)
1363         {
1364         if (!isspace(s[end]))
1365             break;
1366         }
1367     //trace("begin:%d  end:%d", begin, end);
1369     String res = s.substr(begin, end-begin+1);
1370     return res;
1374 int Parser::countLines(int begin, int end)
1376     int count = 0;
1377     for (int i=begin ; i<end ; i++)
1378         {
1379         XMLCh ch = parsebuf[i];
1380         if (ch == '\n' || ch == '\r')
1381             count++;
1382         }
1383     return count;
1387 void Parser::getLineAndColumn(int pos, int *lineNr, int *colNr)
1389     int line = 1;
1390     int col  = 1;
1391     for (long i=0 ; i<pos ; i++)
1392         {
1393         XMLCh ch = parsebuf[i];
1394         if (ch == '\n' || ch == '\r')
1395             {
1396             col = 0;
1397             line ++;
1398             }
1399         else
1400             col++;
1401         }
1402     *lineNr = line;
1403     *colNr  = col;
1408 void Parser::error(char *fmt, ...)
1410     int lineNr;
1411     int colNr;
1412     getLineAndColumn(currentPosition, &lineNr, &colNr);
1413     va_list args;
1414     fprintf(stderr, "xml error at line %d, column %d:", lineNr, colNr);
1415     va_start(args,fmt);
1416     vfprintf(stderr,fmt,args);
1417     va_end(args) ;
1418     fprintf(stderr, "\n");
1423 int Parser::peek(int pos)
1425     if (pos >= parselen)
1426         return -1;
1427     currentPosition = pos;
1428     int ch = parsebuf[pos];
1429     //printf("ch:%c\n", ch);
1430     return ch;
1435 String Parser::encode(const String &str)
1437     String ret;
1438     for (unsigned int i=0 ; i<str.size() ; i++)
1439         {
1440         XMLCh ch = (XMLCh)str[i];
1441         if (ch == '&')
1442             ret.append("&amp;");
1443         else if (ch == '<')
1444             ret.append("&lt;");
1445         else if (ch == '>')
1446             ret.append("&gt;");
1447         else if (ch == '\'')
1448             ret.append("&apos;");
1449         else if (ch == '"')
1450             ret.append("&quot;");
1451         else
1452             ret.push_back(ch);
1454         }
1455     return ret;
1459 int Parser::match(int p0, const char *text)
1461     int p = p0;
1462     while (*text)
1463         {
1464         if (peek(p) != *text)
1465             return p0;
1466         p++; text++;
1467         }
1468     return p;
1473 int Parser::skipwhite(int p)
1476     while (p<parselen)
1477         {
1478         int p2 = match(p, "<!--");
1479         if (p2 > p)
1480             {
1481             p = p2;
1482             while (p<parselen)
1483               {
1484               p2 = match(p, "-->");
1485               if (p2 > p)
1486                   {
1487                   p = p2;
1488                   break;
1489                   }
1490               p++;
1491               }
1492           }
1493       XMLCh b = peek(p);
1494       if (!isspace(b))
1495           break;
1496       p++;
1497       }
1498   return p;
1501 /* modify this to allow all chars for an element or attribute name*/
1502 int Parser::getWord(int p0, String &buf)
1504     int p = p0;
1505     while (p<parselen)
1506         {
1507         XMLCh b = peek(p);
1508         if (b<=' ' || b=='/' || b=='>' || b=='=')
1509             break;
1510         buf.push_back(b);
1511         p++;
1512         }
1513     return p;
1516 int Parser::getQuoted(int p0, String &buf, int do_i_parse)
1519     int p = p0;
1520     if (peek(p) != '"' && peek(p) != '\'')
1521         return p0;
1522     p++;
1524     while ( p<parselen )
1525         {
1526         XMLCh b = peek(p);
1527         if (b=='"' || b=='\'')
1528             break;
1529         if (b=='&' && do_i_parse)
1530             {
1531             bool found = false;
1532             for (EntityEntry *ee = entities ; ee->value ; ee++)
1533                 {
1534                 int p2 = match(p, ee->escaped);
1535                 if (p2>p)
1536                     {
1537                     buf.push_back(ee->value);
1538                     p = p2;
1539                     found = true;
1540                     break;
1541                     }
1542                 }
1543             if (!found)
1544                 {
1545                 error("unterminated entity");
1546                 return false;
1547                 }
1548             }
1549         else
1550             {
1551             buf.push_back(b);
1552             p++;
1553             }
1554         }
1555     return p;
1558 int Parser::parseVersion(int p0)
1560     //printf("### parseVersion: %d\n", p0);
1562     int p = p0;
1564     p = skipwhite(p0);
1566     if (peek(p) != '<')
1567         return p0;
1569     p++;
1570     if (p>=parselen || peek(p)!='?')
1571         return p0;
1573     p++;
1575     String buf;
1577     while (p<parselen)
1578         {
1579         XMLCh ch = peek(p);
1580         if (ch=='?')
1581             {
1582             p++;
1583             break;
1584             }
1585         buf.push_back(ch);
1586         p++;
1587         }
1589     if (peek(p) != '>')
1590         return p0;
1591     p++;
1593     //printf("Got version:%s\n",buf.c_str());
1594     return p;
1597 int Parser::parseDoctype(int p0)
1599     //printf("### parseDoctype: %d\n", p0);
1601     int p = p0;
1602     p = skipwhite(p);
1604     if (p>=parselen || peek(p)!='<')
1605         return p0;
1607     p++;
1609     if (peek(p)!='!' || peek(p+1)=='-')
1610         return p0;
1611     p++;
1613     String buf;
1614     while (p<parselen)
1615         {
1616         XMLCh ch = peek(p);
1617         if (ch=='>')
1618             {
1619             p++;
1620             break;
1621             }
1622         buf.push_back(ch);
1623         p++;
1624         }
1626     //printf("Got doctype:%s\n",buf.c_str());
1627     return p;
1632 int Parser::parseElement(int p0, Element *par,int lineNr)
1635     int p = p0;
1637     int p2 = p;
1639     p = skipwhite(p);
1641     //## Get open tag
1642     XMLCh ch = peek(p);
1643     if (ch!='<')
1644         return p0;
1646     int line, col;
1647     //getLineAndColumn(p, &line, &col);
1649     p++;
1651     String openTagName;
1652     p = skipwhite(p);
1653     p = getWord(p, openTagName);
1654     //printf("####tag :%s\n", openTagName.c_str());
1655     p = skipwhite(p);
1657     //Add element to tree
1658     Element *n = new Element(openTagName);
1659     n->line = lineNr + countLines(p0, p);
1660     n->parent = par;
1661     par->addChild(n);
1663     // Get attributes
1664     if (peek(p) != '>')
1665         {
1666         while (p<parselen)
1667             {
1668             p = skipwhite(p);
1669             ch = peek(p);
1670             //printf("ch:%c\n",ch);
1671             if (ch=='>')
1672                 break;
1673             else if (ch=='/' && p<parselen+1)
1674                 {
1675                 p++;
1676                 p = skipwhite(p);
1677                 ch = peek(p);
1678                 if (ch=='>')
1679                     {
1680                     p++;
1681                     //printf("quick close\n");
1682                     return p;
1683                     }
1684                 }
1685             String attrName;
1686             p2 = getWord(p, attrName);
1687             if (p2==p)
1688                 break;
1689             //printf("name:%s",buf);
1690             p=p2;
1691             p = skipwhite(p);
1692             ch = peek(p);
1693             //printf("ch:%c\n",ch);
1694             if (ch!='=')
1695                 break;
1696             p++;
1697             p = skipwhite(p);
1698             // ch = parsebuf[p];
1699             // printf("ch:%c\n",ch);
1700             String attrVal;
1701             p2 = getQuoted(p, attrVal, true);
1702             p=p2+1;
1703             //printf("name:'%s'   value:'%s'\n",attrName.c_str(),attrVal.c_str());
1704             char *namestr = (char *)attrName.c_str();
1705             if (strncmp(namestr, "xmlns:", 6)==0)
1706                 n->addNamespace(attrName, attrVal);
1707             else
1708                 n->addAttribute(attrName, attrVal);
1709             }
1710         }
1712     bool cdata = false;
1714     p++;
1715     // ### Get intervening data ### */
1716     String data;
1717     while (p<parselen)
1718         {
1719         //# COMMENT
1720         p2 = match(p, "<!--");
1721         if (!cdata && p2>p)
1722             {
1723             p = p2;
1724             while (p<parselen)
1725                 {
1726                 p2 = match(p, "-->");
1727                 if (p2 > p)
1728                     {
1729                     p = p2;
1730                     break;
1731                     }
1732                 p++;
1733                 }
1734             }
1736         ch = peek(p);
1737         //# END TAG
1738         if (ch=='<' && !cdata && peek(p+1)=='/')
1739             {
1740             break;
1741             }
1742         //# CDATA
1743         p2 = match(p, "<![CDATA[");
1744         if (p2 > p)
1745             {
1746             cdata = true;
1747             p = p2;
1748             continue;
1749             }
1751         //# CHILD ELEMENT
1752         if (ch == '<')
1753             {
1754             p2 = parseElement(p, n, lineNr + countLines(p0, p));
1755             if (p2 == p)
1756                 {
1757                 /*
1758                 printf("problem on element:%s.  p2:%d p:%d\n",
1759                       openTagName.c_str(), p2, p);
1760                 */
1761                 return p0;
1762                 }
1763             p = p2;
1764             continue;
1765             }
1766         //# ENTITY
1767         if (ch=='&' && !cdata)
1768             {
1769             bool found = false;
1770             for (EntityEntry *ee = entities ; ee->value ; ee++)
1771                 {
1772                 int p2 = match(p, ee->escaped);
1773                 if (p2>p)
1774                     {
1775                     data.push_back(ee->value);
1776                     p = p2;
1777                     found = true;
1778                     break;
1779                     }
1780                 }
1781             if (!found)
1782                 {
1783                 error("unterminated entity");
1784                 return -1;
1785                 }
1786             continue;
1787             }
1789         //# NONE OF THE ABOVE
1790         data.push_back(ch);
1791         p++;
1792         }/*while*/
1795     n->value = data;
1796     //printf("%d : data:%s\n",p,data.c_str());
1798     //## Get close tag
1799     p = skipwhite(p);
1800     ch = peek(p);
1801     if (ch != '<')
1802         {
1803         error("no < for end tag\n");
1804         return p0;
1805         }
1806     p++;
1807     ch = peek(p);
1808     if (ch != '/')
1809         {
1810         error("no / on end tag");
1811         return p0;
1812         }
1813     p++;
1814     ch = peek(p);
1815     p = skipwhite(p);
1816     String closeTagName;
1817     p = getWord(p, closeTagName);
1818     if (openTagName != closeTagName)
1819         {
1820         error("Mismatched closing tag.  Expected </%S>. Got '%S'.",
1821                 openTagName.c_str(), closeTagName.c_str());
1822         return p0;
1823         }
1824     p = skipwhite(p);
1825     if (peek(p) != '>')
1826         {
1827         error("no > on end tag for '%s'", closeTagName.c_str());
1828         return p0;
1829         }
1830     p++;
1831     // printf("close element:%s\n",closeTagName.c_str());
1832     p = skipwhite(p);
1833     return p;
1839 Element *Parser::parse(XMLCh *buf,int pos,int len)
1841     parselen = len;
1842     parsebuf = buf;
1843     Element *rootNode = new Element("root");
1844     pos = parseVersion(pos);
1845     pos = parseDoctype(pos);
1846     pos = parseElement(pos, rootNode, 1);
1847     return rootNode;
1851 Element *Parser::parse(const char *buf, int pos, int len)
1853     XMLCh *charbuf = new XMLCh[len + 1];
1854     long i = 0;
1855     for ( ; i < len ; i++)
1856         charbuf[i] = (XMLCh)buf[i];
1857     charbuf[i] = '\0';
1859     Element *n = parse(charbuf, pos, len);
1860     delete[] charbuf;
1861     return n;
1864 Element *Parser::parse(const String &buf)
1866     long len = (long)buf.size();
1867     XMLCh *charbuf = new XMLCh[len + 1];
1868     long i = 0;
1869     for ( ; i < len ; i++)
1870         charbuf[i] = (XMLCh)buf[i];
1871     charbuf[i] = '\0';
1873     Element *n = parse(charbuf, 0, len);
1874     delete[] charbuf;
1875     return n;
1878 Element *Parser::parseFile(const String &fileName)
1881     //##### LOAD INTO A CHAR BUF, THEN CONVERT TO XMLCh
1882     FILE *f = fopen(fileName.c_str(), "rb");
1883     if (!f)
1884         return NULL;
1886     struct stat  statBuf;
1887     if (fstat(fileno(f),&statBuf)<0)
1888         {
1889         fclose(f);
1890         return NULL;
1891         }
1892     long filelen = statBuf.st_size;
1894     //printf("length:%d\n",filelen);
1895     XMLCh *charbuf = new XMLCh[filelen + 1];
1896     for (XMLCh *p=charbuf ; !feof(f) ; p++)
1897         {
1898         *p = (XMLCh)fgetc(f);
1899         }
1900     fclose(f);
1901     charbuf[filelen] = '\0';
1904     /*
1905     printf("nrbytes:%d\n",wc_count);
1906     printf("buf:%ls\n======\n",charbuf);
1907     */
1908     Element *n = parse(charbuf, 0, filelen);
1909     delete[] charbuf;
1910     return n;
1913 //########################################################################
1914 //########################################################################
1915 //##  E N D    X M L
1916 //########################################################################
1917 //########################################################################
1924 //########################################################################
1925 //########################################################################
1926 //##  U R I
1927 //########################################################################
1928 //########################################################################
1930 //This would normally be a call to a UNICODE function
1931 #define isLetter(x) isalpha(x)
1933 /**
1934  *  A class that implements the W3C URI resource reference.
1935  */
1936 class URI
1938 public:
1940     typedef enum
1941         {
1942         SCHEME_NONE =0,
1943         SCHEME_DATA,
1944         SCHEME_HTTP,
1945         SCHEME_HTTPS,
1946         SCHEME_FTP,
1947         SCHEME_FILE,
1948         SCHEME_LDAP,
1949         SCHEME_MAILTO,
1950         SCHEME_NEWS,
1951         SCHEME_TELNET
1952         } SchemeTypes;
1954     /**
1955      *
1956      */
1957     URI()
1958         {
1959         init();
1960         }
1962     /**
1963      *
1964      */
1965     URI(const String &str)
1966         {
1967         init();
1968         parse(str);
1969         }
1972     /**
1973      *
1974      */
1975     URI(const char *str)
1976         {
1977         init();
1978         String domStr = str;
1979         parse(domStr);
1980         }
1983     /**
1984      *
1985      */
1986     URI(const URI &other)
1987         {
1988         init();
1989         assign(other);
1990         }
1993     /**
1994      *
1995      */
1996     URI &operator=(const URI &other)
1997         {
1998         init();
1999         assign(other);
2000         return *this;
2001         }
2004     /**
2005      *
2006      */
2007     virtual ~URI()
2008         {}
2012     /**
2013      *
2014      */
2015     virtual bool parse(const String &str);
2017     /**
2018      *
2019      */
2020     virtual String toString() const;
2022     /**
2023      *
2024      */
2025     virtual int getScheme() const;
2027     /**
2028      *
2029      */
2030     virtual String getSchemeStr() const;
2032     /**
2033      *
2034      */
2035     virtual String getAuthority() const;
2037     /**
2038      *  Same as getAuthority, but if the port has been specified
2039      *  as host:port , the port will not be included
2040      */
2041     virtual String getHost() const;
2043     /**
2044      *
2045      */
2046     virtual int getPort() const;
2048     /**
2049      *
2050      */
2051     virtual String getPath() const;
2053     /**
2054      *
2055      */
2056     virtual String getNativePath() const;
2058     /**
2059      *
2060      */
2061     virtual bool isAbsolute() const;
2063     /**
2064      *
2065      */
2066     virtual bool isOpaque() const;
2068     /**
2069      *
2070      */
2071     virtual String getQuery() const;
2073     /**
2074      *
2075      */
2076     virtual String getFragment() const;
2078     /**
2079      *
2080      */
2081     virtual URI resolve(const URI &other) const;
2083     /**
2084      *
2085      */
2086     virtual void normalize();
2088 private:
2090     /**
2091      *
2092      */
2093     void init()
2094         {
2095         parsebuf  = NULL;
2096         parselen  = 0;
2097         scheme    = SCHEME_NONE;
2098         schemeStr = "";
2099         port      = 0;
2100         authority = "";
2101         path      = "";
2102         absolute  = false;
2103         opaque    = false;
2104         query     = "";
2105         fragment  = "";
2106         }
2109     /**
2110      *
2111      */
2112     void assign(const URI &other)
2113         {
2114         scheme    = other.scheme;
2115         schemeStr = other.schemeStr;
2116         authority = other.authority;
2117         port      = other.port;
2118         path      = other.path;
2119         absolute  = other.absolute;
2120         opaque    = other.opaque;
2121         query     = other.query;
2122         fragment  = other.fragment;
2123         }
2125     int scheme;
2127     String schemeStr;
2129     String authority;
2131     bool portSpecified;
2133     int port;
2135     String path;
2137     bool absolute;
2139     bool opaque;
2141     String query;
2143     String fragment;
2145     void error(const char *fmt, ...);
2147     void trace(const char *fmt, ...);
2150     int peek(int p);
2152     int match(int p, char *key);
2154     int parseScheme(int p);
2156     int parseHierarchicalPart(int p0);
2158     int parseQuery(int p0);
2160     int parseFragment(int p0);
2162     int parse(int p);
2164     char *parsebuf;
2166     int parselen;
2168 };
2172 typedef struct
2174     int  ival;
2175     char *sval;
2176     int  port;
2177 } LookupEntry;
2179 LookupEntry schemes[] =
2181     { URI::SCHEME_DATA,   "data:",    0 },
2182     { URI::SCHEME_HTTP,   "http:",   80 },
2183     { URI::SCHEME_HTTPS,  "https:", 443 },
2184     { URI::SCHEME_FTP,    "ftp",     12 },
2185     { URI::SCHEME_FILE,   "file:",    0 },
2186     { URI::SCHEME_LDAP,   "ldap:",  123 },
2187     { URI::SCHEME_MAILTO, "mailto:", 25 },
2188     { URI::SCHEME_NEWS,   "news:",  117 },
2189     { URI::SCHEME_TELNET, "telnet:", 23 },
2190     { 0,                  NULL,       0 }
2191 };
2194 String URI::toString() const
2196     String str = schemeStr;
2197     if (authority.size() > 0)
2198         {
2199         str.append("//");
2200         str.append(authority);
2201         }
2202     str.append(path);
2203     if (query.size() > 0)
2204         {
2205         str.append("?");
2206         str.append(query);
2207         }
2208     if (fragment.size() > 0)
2209         {
2210         str.append("#");
2211         str.append(fragment);
2212         }
2213     return str;
2217 int URI::getScheme() const
2219     return scheme;
2222 String URI::getSchemeStr() const
2224     return schemeStr;
2228 String URI::getAuthority() const
2230     String ret = authority;
2231     if (portSpecified && port>=0)
2232         {
2233         char buf[7];
2234         snprintf(buf, 6, ":%6d", port);
2235         ret.append(buf);
2236         }
2237     return ret;
2240 String URI::getHost() const
2242     return authority;
2245 int URI::getPort() const
2247     return port;
2251 String URI::getPath() const
2253     return path;
2256 String URI::getNativePath() const
2258     String npath;
2259 #ifdef __WIN32__
2260     unsigned int firstChar = 0;
2261     if (path.size() >= 3)
2262         {
2263         if (path[0] == '/' &&
2264             isLetter(path[1]) &&
2265             path[2] == ':')
2266             firstChar++;
2267          }
2268     for (unsigned int i=firstChar ; i<path.size() ; i++)
2269         {
2270         XMLCh ch = (XMLCh) path[i];
2271         if (ch == '/')
2272             npath.push_back((XMLCh)'\\');
2273         else
2274             npath.push_back(ch);
2275         }
2276 #else
2277     npath = path;
2278 #endif
2279     return npath;
2283 bool URI::isAbsolute() const
2285     return absolute;
2288 bool URI::isOpaque() const
2290     return opaque;
2294 String URI::getQuery() const
2296     return query;
2300 String URI::getFragment() const
2302     return fragment;
2306 URI URI::resolve(const URI &other) const
2308     //### According to w3c, this is handled in 3 cases
2310     //## 1
2311     if (opaque || other.isAbsolute())
2312         return other;
2314     //## 2
2315     if (other.fragment.size()  >  0 &&
2316         other.path.size()      == 0 &&
2317         other.scheme           == SCHEME_NONE &&
2318         other.authority.size() == 0 &&
2319         other.query.size()     == 0 )
2320         {
2321         URI fragUri = *this;
2322         fragUri.fragment = other.fragment;
2323         return fragUri;
2324         }
2326     //## 3 http://www.ietf.org/rfc/rfc2396.txt, section 5.2
2327     URI newUri;
2328     //# 3.1
2329     newUri.scheme    = scheme;
2330     newUri.schemeStr = schemeStr;
2331     newUri.query     = other.query;
2332     newUri.fragment  = other.fragment;
2333     if (other.authority.size() > 0)
2334         {
2335         //# 3.2
2336         if (absolute || other.absolute)
2337             newUri.absolute = true;
2338         newUri.authority = other.authority;
2339         newUri.port      = other.port;//part of authority
2340         newUri.path      = other.path;
2341         }
2342     else
2343         {
2344         //# 3.3
2345         if (other.absolute)
2346             {
2347             newUri.absolute = true;
2348             newUri.path     = other.path;
2349             }
2350         else
2351             {
2352             unsigned int pos = path.find_last_of('/');
2353             if (pos != path.npos)
2354                 {
2355                 String tpath = path.substr(0, pos+1);
2356                 tpath.append(other.path);
2357                 newUri.path = tpath;
2358                 }
2359             else
2360                 newUri.path = other.path;
2361             }
2362         }
2364     newUri.normalize();
2365     return newUri;
2370 /**
2371  *  This follows the Java URI algorithm:
2372  *   1. All "." segments are removed.
2373  *   2. If a ".." segment is preceded by a non-".." segment
2374  *          then both of these segments are removed. This step
2375  *          is repeated until it is no longer applicable.
2376  *   3. If the path is relative, and if its first segment
2377  *          contains a colon character (':'), then a "." segment
2378  *          is prepended. This prevents a relative URI with a path
2379  *          such as "a:b/c/d" from later being re-parsed as an
2380  *          opaque URI with a scheme of "a" and a scheme-specific
2381  *          part of "b/c/d". (Deviation from RFC 2396)
2382  */
2383 void URI::normalize()
2385     std::vector<String> segments;
2387     //## Collect segments
2388     if (path.size()<2)
2389         return;
2390     bool abs = false;
2391     unsigned int pos=0;
2392     if (path[0]=='/')
2393         {
2394         abs = true;
2395         pos++;
2396         }
2397     while (pos < path.size())
2398         {
2399         unsigned int pos2 = path.find('/', pos);
2400         if (pos2==path.npos)
2401             {
2402             String seg = path.substr(pos);
2403             //printf("last segment:%s\n", seg.c_str());
2404             segments.push_back(seg);
2405             break;
2406             }
2407         if (pos2>pos)
2408             {
2409             String seg = path.substr(pos, pos2-pos);
2410             //printf("segment:%s\n", seg.c_str());
2411             segments.push_back(seg);
2412             }
2413         pos = pos2;
2414         pos++;
2415         }
2417     //## Clean up (normalize) segments
2418     bool edited = false;
2419     std::vector<String>::iterator iter;
2420     for (iter=segments.begin() ; iter!=segments.end() ; )
2421         {
2422         String s = *iter;
2423         if (s == ".")
2424             {
2425             iter = segments.erase(iter);
2426             edited = true;
2427             }
2428         else if (s == ".." &&
2429                  iter != segments.begin() &&
2430                  *(iter-1) != "..")
2431             {
2432             iter--; //back up, then erase two entries
2433             iter = segments.erase(iter);
2434             iter = segments.erase(iter);
2435             edited = true;
2436             }
2437         else
2438             iter++;
2439         }
2441     //## Rebuild path, if necessary
2442     if (edited)
2443         {
2444         path.clear();
2445         if (abs)
2446             {
2447             path.append("/");
2448             }
2449         std::vector<String>::iterator iter;
2450         for (iter=segments.begin() ; iter!=segments.end() ; iter++)
2451             {
2452             if (iter != segments.begin())
2453                 path.append("/");
2454             path.append(*iter);
2455             }
2456         }
2462 //#########################################################################
2463 //# M E S S A G E S
2464 //#########################################################################
2466 void URI::error(const char *fmt, ...)
2468     va_list args;
2469     fprintf(stderr, "URI error: ");
2470     va_start(args, fmt);
2471     vfprintf(stderr, fmt, args);
2472     va_end(args);
2473     fprintf(stderr, "\n");
2476 void URI::trace(const char *fmt, ...)
2478     va_list args;
2479     fprintf(stdout, "URI: ");
2480     va_start(args, fmt);
2481     vfprintf(stdout, fmt, args);
2482     va_end(args);
2483     fprintf(stdout, "\n");
2489 //#########################################################################
2490 //# P A R S I N G
2491 //#########################################################################
2495 int URI::peek(int p)
2497     if (p<0 || p>=parselen)
2498         return -1;
2499     return parsebuf[p];
2504 int URI::match(int p0, char *key)
2506     int p = p0;
2507     while (p < parselen)
2508         {
2509         if (*key == '\0')
2510             return p;
2511         else if (*key != parsebuf[p])
2512             break;
2513         p++; key++;
2514         }
2515     return p0;
2518 //#########################################################################
2519 //#  Parsing is performed according to:
2520 //#  http://www.gbiv.com/protocols/uri/rfc/rfc3986.html#components
2521 //#########################################################################
2523 int URI::parseScheme(int p0)
2525     int p = p0;
2526     for (LookupEntry *entry = schemes; entry->sval ; entry++)
2527         {
2528         int p2 = match(p, entry->sval);
2529         if (p2 > p)
2530             {
2531             schemeStr = entry->sval;
2532             scheme    = entry->ival;
2533             port      = entry->port;
2534             p = p2;
2535             return p;
2536             }
2537         }
2539     return p;
2543 int URI::parseHierarchicalPart(int p0)
2545     int p = p0;
2546     int ch;
2548     //# Authority field (host and port, for example)
2549     int p2 = match(p, "//");
2550     if (p2 > p)
2551         {
2552         p = p2;
2553         portSpecified = false;
2554         String portStr;
2555         while (p < parselen)
2556             {
2557             ch = peek(p);
2558             if (ch == '/')
2559                 break;
2560             else if (ch == ':')
2561                 portSpecified = true;
2562             else if (portSpecified)
2563                 portStr.push_back((XMLCh)ch);
2564             else
2565                 authority.push_back((XMLCh)ch);
2566             p++;
2567             }
2568         if (portStr.size() > 0)
2569             {
2570             char *pstr = (char *)portStr.c_str();
2571             char *endStr;
2572             long val = strtol(pstr, &endStr, 10);
2573             if (endStr > pstr) //successful parse?
2574                 port = val;
2575             }
2576         }
2578     //# Are we absolute?
2579     ch = peek(p);
2580     if (isLetter(ch) && peek(p+1)==':')
2581         {
2582         absolute = true;
2583         path.push_back((XMLCh)'/');
2584         }
2585     else if (ch == '/')
2586         {
2587         absolute = true;
2588         if (p>p0) //in other words, if '/' is not the first char
2589             opaque = true;
2590         path.push_back((XMLCh)ch);
2591         p++;
2592         }
2594     while (p < parselen)
2595         {
2596         ch = peek(p);
2597         if (ch == '?' || ch == '#')
2598             break;
2599         path.push_back((XMLCh)ch);
2600         p++;
2601         }
2603     return p;
2606 int URI::parseQuery(int p0)
2608     int p = p0;
2609     int ch = peek(p);
2610     if (ch != '?')
2611         return p0;
2613     p++;
2614     while (p < parselen)
2615         {
2616         ch = peek(p);
2617         if (ch == '#')
2618             break;
2619         query.push_back((XMLCh)ch);
2620         p++;
2621         }
2624     return p;
2627 int URI::parseFragment(int p0)
2630     int p = p0;
2631     int ch = peek(p);
2632     if (ch != '#')
2633         return p0;
2635     p++;
2636     while (p < parselen)
2637         {
2638         ch = peek(p);
2639         if (ch == '?')
2640             break;
2641         fragment.push_back((XMLCh)ch);
2642         p++;
2643         }
2646     return p;
2650 int URI::parse(int p0)
2653     int p = p0;
2655     int p2 = parseScheme(p);
2656     if (p2 < 0)
2657         {
2658         error("Scheme");
2659         return -1;
2660         }
2661     p = p2;
2664     p2 = parseHierarchicalPart(p);
2665     if (p2 < 0)
2666         {
2667         error("Hierarchical part");
2668         return -1;
2669         }
2670     p = p2;
2672     p2 = parseQuery(p);
2673     if (p2 < 0)
2674         {
2675         error("Query");
2676         return -1;
2677         }
2678     p = p2;
2681     p2 = parseFragment(p);
2682     if (p2 < 0)
2683         {
2684         error("Fragment");
2685         return -1;
2686         }
2687     p = p2;
2689     return p;
2695 bool URI::parse(const String &str)
2697     init();
2698     
2699     parselen = str.size();
2701     String tmp;
2702     for (unsigned int i=0 ; i<str.size() ; i++)
2703         {
2704         XMLCh ch = (XMLCh) str[i];
2705         if (ch == '\\')
2706             tmp.push_back((XMLCh)'/');
2707         else
2708             tmp.push_back(ch);
2709         }
2710     parsebuf = (char *) tmp.c_str();
2713     int p = parse(0);
2714     normalize();
2716     if (p < 0)
2717         {
2718         error("Syntax error");
2719         return false;
2720         }
2722     //printf("uri:%s\n", toString().c_str());
2723     //printf("path:%s\n", path.c_str());
2725     return true;
2736 //########################################################################
2737 //########################################################################
2738 //##  M A K E
2739 //########################################################################
2740 //########################################################################
2742 //########################################################################
2743 //# F I L E S E T
2744 //########################################################################
2745 /**
2746  * This is the descriptor for a <fileset> item
2747  */
2748 class FileSet
2750 public:
2752     /**
2753      *
2754      */
2755     FileSet()
2756         {}
2758     /**
2759      *
2760      */
2761     FileSet(const FileSet &other)
2762         { assign(other); }
2764     /**
2765      *
2766      */
2767     FileSet &operator=(const FileSet &other)
2768         { assign(other); return *this; }
2770     /**
2771      *
2772      */
2773     virtual ~FileSet()
2774         {}
2776     /**
2777      *
2778      */
2779     String getDirectory()
2780         { return directory; }
2781         
2782     /**
2783      *
2784      */
2785     void setDirectory(const String &val)
2786         { directory = val; }
2788     /**
2789      *
2790      */
2791     void setFiles(const std::vector<String> &val)
2792         { files = val; }
2794     /**
2795      *
2796      */
2797     std::vector<String> getFiles()
2798         { return files; }
2799         
2800     /**
2801      *
2802      */
2803     void setIncludes(const std::vector<String> &val)
2804         { includes = val; }
2806     /**
2807      *
2808      */
2809     std::vector<String> getIncludes()
2810         { return includes; }
2811         
2812     /**
2813      *
2814      */
2815     void setExcludes(const std::vector<String> &val)
2816         { excludes = val; }
2818     /**
2819      *
2820      */
2821     std::vector<String> getExcludes()
2822         { return excludes; }
2823         
2824     /**
2825      *
2826      */
2827     unsigned int size()
2828         { return files.size(); }
2829         
2830     /**
2831      *
2832      */
2833     String operator[](int index)
2834         { return files[index]; }
2835         
2836     /**
2837      *
2838      */
2839     void clear()
2840         {
2841         directory = "";
2842         files.clear();
2843         includes.clear();
2844         excludes.clear();
2845         }
2846         
2848 private:
2850     void assign(const FileSet &other)
2851         {
2852         directory = other.directory;
2853         files     = other.files;
2854         includes  = other.includes;
2855         excludes  = other.excludes;
2856         }
2858     String directory;
2859     std::vector<String> files;
2860     std::vector<String> includes;
2861     std::vector<String> excludes;
2862 };
2867 //########################################################################
2868 //# M A K E    B A S E
2869 //########################################################################
2870 /**
2871  * Base class for all classes in this file
2872  */
2873 class MakeBase
2875 public:
2877     MakeBase()
2878         { line = 0; }
2879     virtual ~MakeBase()
2880         {}
2882     /**
2883      *     Return the URI of the file associated with this object 
2884      */     
2885     URI getURI()
2886         { return uri; }
2888     /**
2889      * Set the uri to the given string
2890      */
2891     void setURI(const String &uristr)
2892         { uri.parse(uristr); }
2894     /**
2895      *  Resolve another path relative to this one
2896      */
2897     String resolve(const String &otherPath);
2899     /**
2900      *  Get an element attribute, performing substitutions if necessary
2901      */
2902     bool getAttribute(Element *elem, const String &name, String &result);
2904     /**
2905      * Get an element value, performing substitutions if necessary
2906      */
2907     bool getValue(Element *elem, String &result);
2908     
2909     /**
2910      * Set the current line number in the file
2911      */         
2912     void setLine(int val)
2913         { line = val; }
2914         
2915     /**
2916      * Get the current line number in the file
2917      */         
2918     int getLine()
2919         { return line; }
2921 protected:
2923     /**
2924      *    The path to the file associated with this object
2925      */     
2926     URI uri;
2929     /**
2930      *  Print a printf()-like formatted error message
2931      */
2932     void error(char *fmt, ...);
2934     /**
2935      *  Print a printf()-like formatted trace message
2936      */
2937     void status(char *fmt, ...);
2939     /**
2940      *  Print a printf()-like formatted trace message
2941      */
2942     void trace(char *fmt, ...);
2944     /**
2945      *  Check if a given string matches a given regex pattern
2946      */
2947     bool regexMatch(const String &str, const String &pattern);
2949     /**
2950      *
2951      */
2952     String getSuffix(const String &fname);
2954     /**
2955      * Break up a string into substrings delimited the characters
2956      * in delimiters.  Null-length substrings are ignored
2957      */  
2958     std::vector<String> tokenize(const String &val,
2959                           const String &delimiters);
2961     /**
2962      *  replace runs of whitespace with a space
2963      */
2964     String strip(const String &s);
2966     /**
2967      *  remove leading whitespace from each line
2968      */
2969     String leftJustify(const String &s);
2971     /**
2972      *  remove leading and trailing whitespace from string
2973      */
2974     String trim(const String &s);
2976     /**
2977      * Return the native format of the canonical
2978      * path which we store
2979      */
2980     String getNativePath(const String &path);
2982     /**
2983      * Execute a shell command.  Outbuf is a ref to a string
2984      * to catch the result.     
2985      */         
2986     bool executeCommand(const String &call,
2987                         const String &inbuf,
2988                         String &outbuf,
2989                         String &errbuf);
2990     /**
2991      * List all directories in a given base and starting directory
2992      * It is usually called like:
2993      *        bool ret = listDirectories("src", "", result);    
2994      */         
2995     bool listDirectories(const String &baseName,
2996                          const String &dirname,
2997                          std::vector<String> &res);
2999     /**
3000      * Find all files in the named directory 
3001      */         
3002     bool listFiles(const String &baseName,
3003                    const String &dirname,
3004                    std::vector<String> &result);
3006     /**
3007      * Perform a listing for a fileset 
3008      */         
3009     bool listFiles(MakeBase &propRef, FileSet &fileSet);
3011     /**
3012      * Parse a <patternset>
3013      */  
3014     bool parsePatternSet(Element *elem,
3015                        MakeBase &propRef,
3016                        std::vector<String> &includes,
3017                        std::vector<String> &excludes);
3019     /**
3020      * Parse a <fileset> entry, and determine which files
3021      * should be included
3022      */  
3023     bool parseFileSet(Element *elem,
3024                     MakeBase &propRef,
3025                     FileSet &fileSet);
3027     /**
3028      * Return this object's property list
3029      */
3030     virtual std::map<String, String> &getProperties()
3031         { return properties; }
3033     /**
3034      * Return a named property if found, else a null string
3035      */
3036     virtual String getProperty(const String &name)
3037         {
3038         String val;
3039         std::map<String, String>::iterator iter;
3040         iter = properties.find(name);
3041         if (iter != properties.end())
3042             val = iter->second;
3043         return val;
3044         }
3047     std::map<String, String> properties;
3049     /**
3050      * Turn 'true' and 'false' into boolean values
3051      */             
3052     bool getBool(const String &str, bool &val);
3054     /**
3055      * Create a directory, making intermediate dirs
3056      * if necessary
3057      */                  
3058     bool createDirectory(const String &dirname);
3060     /**
3061      * Delete a directory and its children if desired
3062      */
3063     bool removeDirectory(const String &dirName);
3065     /**
3066      * Copy a file from one name to another. Perform only if needed
3067      */ 
3068     bool copyFile(const String &srcFile, const String &destFile);
3070     /**
3071      * Tests if the file exists and is a regular file
3072      */ 
3073     bool isRegularFile(const String &fileName);
3075     /**
3076      * Tests if the file exists and is a directory
3077      */ 
3078     bool isDirectory(const String &fileName);
3080     /**
3081      * Tests is the modification date of fileA is newer than fileB
3082      */ 
3083     bool isNewerThan(const String &fileA, const String &fileB);
3085 private:
3087     /**
3088      * replace variable refs like ${a} with their values
3089      */         
3090     bool getSubstitutions(const String &s, String &result);
3092     int line;
3095 };
3100 /**
3101  *  Print a printf()-like formatted error message
3102  */
3103 void MakeBase::error(char *fmt, ...)
3105     va_list args;
3106     va_start(args,fmt);
3107     fprintf(stderr, "Make error line %d: ", line);
3108     vfprintf(stderr, fmt, args);
3109     fprintf(stderr, "\n");
3110     va_end(args) ;
3115 /**
3116  *  Print a printf()-like formatted trace message
3117  */
3118 void MakeBase::status(char *fmt, ...)
3120     va_list args;
3121     va_start(args,fmt);
3122     //fprintf(stdout, " ");
3123     vfprintf(stdout, fmt, args);
3124     fprintf(stdout, "\n");
3125     va_end(args) ;
3130 /**
3131  *  Resolve another path relative to this one
3132  */
3133 String MakeBase::resolve(const String &otherPath)
3135     URI otherURI(otherPath);
3136     URI fullURI = uri.resolve(otherURI);
3137     String ret = fullURI.toString();
3138     return ret;
3142 /**
3143  *  Print a printf()-like formatted trace message
3144  */
3145 void MakeBase::trace(char *fmt, ...)
3147     va_list args;
3148     va_start(args,fmt);
3149     fprintf(stdout, "Make: ");
3150     vfprintf(stdout, fmt, args);
3151     fprintf(stdout, "\n");
3152     va_end(args) ;
3157 /**
3158  *  Check if a given string matches a given regex pattern
3159  */
3160 bool MakeBase::regexMatch(const String &str, const String &pattern)
3162     const TRexChar *terror = NULL;
3163     const TRexChar *cpat = pattern.c_str();
3164     TRex *expr = trex_compile(cpat, &terror);
3165     if (!expr)
3166         {
3167         if (!terror)
3168             terror = "undefined";
3169         error("compilation error [%s]!\n", terror);
3170         return false;
3171         } 
3173     bool ret = true;
3175     const TRexChar *cstr = str.c_str();
3176     if (trex_match(expr, cstr))
3177         {
3178         ret = true;
3179         }
3180     else
3181         {
3182         ret = false;
3183         }
3185     trex_free(expr);
3187     return ret;
3190 /**
3191  *  Return the suffix, if any, of a file name
3192  */
3193 String MakeBase::getSuffix(const String &fname)
3195     if (fname.size() < 2)
3196         return "";
3197     unsigned int pos = fname.find_last_of('.');
3198     if (pos == fname.npos)
3199         return "";
3200     pos++;
3201     String res = fname.substr(pos, fname.size()-pos);
3202     //trace("suffix:%s", res.c_str()); 
3203     return res;
3208 /**
3209  * Break up a string into substrings delimited the characters
3210  * in delimiters.  Null-length substrings are ignored
3211  */  
3212 std::vector<String> MakeBase::tokenize(const String &str,
3213                                 const String &delimiters)
3216     std::vector<String> res;
3217     char *del = (char *)delimiters.c_str();
3218     String dmp;
3219     for (unsigned int i=0 ; i<str.size() ; i++)
3220         {
3221         char ch = str[i];
3222         char *p = (char *)0;
3223         for (p=del ; *p ; p++)
3224             if (*p == ch)
3225                 break;
3226         if (*p)
3227             {
3228             if (dmp.size() > 0)
3229                 {
3230                 res.push_back(dmp);
3231                 dmp.clear();
3232                 }
3233             }
3234         else
3235             {
3236             dmp.push_back(ch);
3237             }
3238         }
3239     //Add tail
3240     if (dmp.size() > 0)
3241         {
3242         res.push_back(dmp);
3243         dmp.clear();
3244         }
3246     return res;
3251 /**
3252  *  replace runs of whitespace with a single space
3253  */
3254 String MakeBase::strip(const String &s)
3256     int len = s.size();
3257     String stripped;
3258     for (int i = 0 ; i<len ; i++)
3259         {
3260         char ch = s[i];
3261         if (isspace(ch))
3262             {
3263             stripped.push_back(' ');
3264             for ( ; i<len ; i++)
3265                 {
3266                 ch = s[i];
3267                 if (!isspace(ch))
3268                     {
3269                     stripped.push_back(ch);
3270                     break;
3271                     }
3272                 }
3273             }
3274         else
3275             {
3276             stripped.push_back(ch);
3277             }
3278         }
3279     return stripped;
3282 /**
3283  *  remove leading whitespace from each line
3284  */
3285 String MakeBase::leftJustify(const String &s)
3287     String out;
3288     int len = s.size();
3289     for (int i = 0 ; i<len ; )
3290         {
3291         char ch;
3292         //Skip to first visible character
3293         while (i<len)
3294             {
3295             ch = s[i];
3296             if (ch == '\n' || ch == '\r'
3297               || !isspace(ch))
3298                   break;
3299             i++;
3300             }
3301         //Copy the rest of the line
3302         while (i<len)
3303             {
3304             ch = s[i];
3305             if (ch == '\n' || ch == '\r')
3306                 {
3307                 if (ch != '\r')
3308                     out.push_back('\n');
3309                 i++;
3310                 break;
3311                 }
3312             else
3313                 {
3314                 out.push_back(ch);
3315                 }
3316             i++;
3317             }
3318         }
3319     return out;
3323 /**
3324  *  Removes whitespace from beginning and end of a string
3325  */
3326 String MakeBase::trim(const String &s)
3328     if (s.size() < 1)
3329         return s;
3330     
3331     //Find first non-ws char
3332     unsigned int begin = 0;
3333     for ( ; begin < s.size() ; begin++)
3334         {
3335         if (!isspace(s[begin]))
3336             break;
3337         }
3339     //Find first non-ws char, going in reverse
3340     unsigned int end = s.size() - 1;
3341     for ( ; end > begin ; end--)
3342         {
3343         if (!isspace(s[end]))
3344             break;
3345         }
3346     //trace("begin:%d  end:%d", begin, end);
3348     String res = s.substr(begin, end-begin+1);
3349     return res;
3352 /**
3353  * Return the native format of the canonical
3354  * path which we store
3355  */
3356 String MakeBase::getNativePath(const String &path)
3358 #ifdef __WIN32__
3359     String npath;
3360     unsigned int firstChar = 0;
3361     if (path.size() >= 3)
3362         {
3363         if (path[0] == '/' &&
3364             isalpha(path[1]) &&
3365             path[2] == ':')
3366             firstChar++;
3367         }
3368     for (unsigned int i=firstChar ; i<path.size() ; i++)
3369         {
3370         char ch = path[i];
3371         if (ch == '/')
3372             npath.push_back('\\');
3373         else
3374             npath.push_back(ch);
3375         }
3376     return npath;
3377 #else
3378     return path;
3379 #endif
3383 #ifdef __WIN32__
3384 #include <tchar.h>
3386 static String win32LastError()
3389     DWORD dw = GetLastError(); 
3391     LPVOID str;
3392     FormatMessage(
3393         FORMAT_MESSAGE_ALLOCATE_BUFFER | 
3394         FORMAT_MESSAGE_FROM_SYSTEM,
3395         NULL,
3396         dw,
3397         0,
3398         (LPTSTR) &str,
3399         0, NULL );
3400     LPTSTR p = _tcschr((const char *)str, _T('\r'));
3401     if(p != NULL)
3402         { // lose CRLF
3403         *p = _T('\0');
3404         }
3405     String ret = (char *)str;
3406     LocalFree(str);
3408     return ret;
3410 #endif
3414 /**
3415  * Execute a system call, using pipes to send data to the
3416  * program's stdin,  and reading stdout and stderr.
3417  */
3418 bool MakeBase::executeCommand(const String &command,
3419                               const String &inbuf,
3420                               String &outbuf,
3421                               String &errbuf)
3424     status("============ cmd ============\n%s\n=============================",
3425                 command.c_str());
3427     outbuf.clear();
3428     errbuf.clear();
3429     
3430 #ifdef __WIN32__
3432     /*
3433     I really hate having win32 code in this program, but the
3434     read buffer in command.com and cmd.exe are just too small
3435     for the large commands we need for compiling and linking.
3436     */
3438     bool ret = true;
3440     //# Allocate a separate buffer for safety
3441     char *paramBuf = new char[command.size() + 1];
3442     if (!paramBuf)
3443        {
3444        error("executeCommand cannot allocate command buffer");
3445        return false;
3446        }
3447     strcpy(paramBuf, (char *)command.c_str());
3449     //# Go to http://msdn2.microsoft.com/en-us/library/ms682499.aspx
3450     //# to see how Win32 pipes work
3452     //# Create pipes
3453     SECURITY_ATTRIBUTES saAttr; 
3454     saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); 
3455     saAttr.bInheritHandle = TRUE; 
3456     saAttr.lpSecurityDescriptor = NULL; 
3457     HANDLE stdinRead,  stdinWrite;
3458     HANDLE stdoutRead, stdoutWrite;
3459     HANDLE stderrRead, stderrWrite;
3460     if (!CreatePipe(&stdinRead, &stdinWrite, &saAttr, 0))
3461         {
3462         error("executeProgram: could not create pipe");
3463         delete[] paramBuf;
3464         return false;
3465         } 
3466     SetHandleInformation(stdinWrite, HANDLE_FLAG_INHERIT, 0);
3467     if (!CreatePipe(&stdoutRead, &stdoutWrite, &saAttr, 0))
3468         {
3469         error("executeProgram: could not create pipe");
3470         delete[] paramBuf;
3471         return false;
3472         } 
3473     SetHandleInformation(stdoutRead, HANDLE_FLAG_INHERIT, 0);
3474     if (!CreatePipe(&stderrRead, &stderrWrite, &saAttr, 0))
3475         {
3476         error("executeProgram: could not create pipe");
3477         delete[] paramBuf;
3478         return false;
3479         } 
3480     SetHandleInformation(stderrRead, HANDLE_FLAG_INHERIT, 0);
3482     // Create the process
3483     STARTUPINFO siStartupInfo;
3484     PROCESS_INFORMATION piProcessInfo;
3485     memset(&siStartupInfo, 0, sizeof(siStartupInfo));
3486     memset(&piProcessInfo, 0, sizeof(piProcessInfo));
3487     siStartupInfo.cb = sizeof(siStartupInfo);
3488     siStartupInfo.hStdError   =  stderrWrite;
3489     siStartupInfo.hStdOutput  =  stdoutWrite;
3490     siStartupInfo.hStdInput   =  stdinRead;
3491     siStartupInfo.dwFlags    |=  STARTF_USESTDHANDLES;
3492    
3493     if (!CreateProcess(NULL, paramBuf, NULL, NULL, true,
3494                 0, NULL, NULL, &siStartupInfo,
3495                 &piProcessInfo))
3496         {
3497         error("executeCommand : could not create process : %s",
3498                     win32LastError().c_str());
3499         ret = false;
3500         }
3502     delete[] paramBuf;
3504     DWORD bytesWritten;
3505     if (inbuf.size()>0 &&
3506         !WriteFile(stdinWrite, inbuf.c_str(), inbuf.size(), 
3507                &bytesWritten, NULL))
3508         {
3509         error("executeCommand: could not write to pipe");
3510         return false;
3511         }    
3512     if (!CloseHandle(stdinWrite))
3513         {          
3514         error("executeCommand: could not close write pipe");
3515         return false;
3516         }
3517     if (!CloseHandle(stdoutWrite))
3518         {
3519         error("executeCommand: could not close read pipe");
3520         return false;
3521         }
3522     if (!CloseHandle(stderrWrite))
3523         {
3524         error("executeCommand: could not close read pipe");
3525         return false;
3526         }
3528     bool lastLoop = false;
3529     while (true)
3530         {
3531         DWORD avail;
3532         DWORD bytesRead;
3533         char readBuf[4096];
3535         //trace("## stderr");
3536         PeekNamedPipe(stderrRead, NULL, 0, NULL, &avail, NULL);
3537         if (avail > 0)
3538             {
3539             bytesRead = 0;
3540             if (avail>4096) avail = 4096;
3541             ReadFile(stderrRead, readBuf, avail, &bytesRead, NULL);
3542             if (bytesRead > 0)
3543                 {
3544                 for (unsigned int i=0 ; i<bytesRead ; i++)
3545                     errbuf.push_back(readBuf[i]);
3546                 }
3547             }
3549         //trace("## stdout");
3550         PeekNamedPipe(stdoutRead, NULL, 0, NULL, &avail, NULL);
3551         if (avail > 0)
3552             {
3553             bytesRead = 0;
3554             if (avail>4096) avail = 4096;
3555             ReadFile(stdoutRead, readBuf, avail, &bytesRead, NULL);
3556             if (bytesRead > 0)
3557                 {
3558                 for (unsigned int i=0 ; i<bytesRead ; i++)
3559                     outbuf.push_back(readBuf[i]);
3560                 }
3561             }
3562             
3563         //Was this the final check after program done?
3564         if (lastLoop)
3565             break;
3567         DWORD exitCode;
3568         GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
3569         if (exitCode != STILL_ACTIVE)
3570             lastLoop = true;
3572         Sleep(10);
3573         }    
3574     //trace("outbuf:%s", outbuf.c_str());
3575     if (!CloseHandle(stdoutRead))
3576         {
3577         error("executeCommand: could not close read pipe");
3578         return false;
3579         }
3580     if (!CloseHandle(stderrRead))
3581         {
3582         error("executeCommand: could not close read pipe");
3583         return false;
3584         }
3586     DWORD exitCode;
3587     GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
3588     //trace("exit code:%d", exitCode);
3589     if (exitCode != 0)
3590         {
3591         ret = false;
3592         }
3593     
3594     CloseHandle(piProcessInfo.hProcess);
3595     CloseHandle(piProcessInfo.hThread);
3597     return ret;
3599 #else //do it unix-style
3601     String s;
3602     FILE *f = popen(command.c_str(), "r");
3603     int errnum = 0;
3604     if (f)
3605         {
3606         while (true)
3607             {
3608             int ch = fgetc(f);
3609             if (ch < 0)
3610                 break;
3611             s.push_back((char)ch);
3612             }
3613         errnum = pclose(f);
3614         }
3615     outbuf = s;
3616     if (errnum != 0)
3617         {
3618         error("exec of command '%s' failed : %s",
3619              command.c_str(), strerror(errno));
3620         return false;
3621         }
3622     else
3623         return true;
3625 #endif
3626
3631 bool MakeBase::listDirectories(const String &baseName,
3632                               const String &dirName,
3633                               std::vector<String> &res)
3635     res.push_back(dirName);
3636     String fullPath = baseName;
3637     if (dirName.size()>0)
3638         {
3639         fullPath.append("/");
3640         fullPath.append(dirName);
3641         }
3642     DIR *dir = opendir(fullPath.c_str());
3643     while (true)
3644         {
3645         struct dirent *de = readdir(dir);
3646         if (!de)
3647             break;
3649         //Get the directory member name
3650         String s = de->d_name;
3651         if (s.size() == 0 || s[0] == '.')
3652             continue;
3653         String childName = dirName;
3654         childName.append("/");
3655         childName.append(s);
3657         String fullChildPath = baseName;
3658         fullChildPath.append("/");
3659         fullChildPath.append(childName);
3660         struct stat finfo;
3661         String childNative = getNativePath(fullChildPath);
3662         if (stat(childNative.c_str(), &finfo)<0)
3663             {
3664             error("cannot stat file:%s", childNative.c_str());
3665             }
3666         else if (S_ISDIR(finfo.st_mode))
3667             {
3668             //trace("directory: %s", childName.c_str());
3669             if (!listDirectories(baseName, childName, res))
3670                 return false;
3671             }
3672         }
3673     closedir(dir);
3675     return true;
3679 bool MakeBase::listFiles(const String &baseDir,
3680                          const String &dirName,
3681                          std::vector<String> &res)
3683     String fullDir = baseDir;
3684     if (dirName.size()>0)
3685         {
3686         fullDir.append("/");
3687         fullDir.append(dirName);
3688         }
3689     String dirNative = getNativePath(fullDir);
3691     std::vector<String> subdirs;
3692     DIR *dir = opendir(dirNative.c_str());
3693     if (!dir)
3694         {
3695         error("Could not open directory %s : %s",
3696               dirNative.c_str(), strerror(errno));
3697         return false;
3698         }
3699     while (true)
3700         {
3701         struct dirent *de = readdir(dir);
3702         if (!de)
3703             break;
3705         //Get the directory member name
3706         String s = de->d_name;
3707         if (s.size() == 0 || s[0] == '.')
3708             continue;
3709         String childName;
3710         if (dirName.size()>0)
3711             {
3712             childName.append(dirName);
3713             childName.append("/");
3714             }
3715         childName.append(s);
3716         String fullChild = baseDir;
3717         fullChild.append("/");
3718         fullChild.append(childName);
3719         
3720         if (isDirectory(fullChild))
3721             {
3722             //trace("directory: %s", childName.c_str());
3723             if (!listFiles(baseDir, childName, res))
3724                 return false;
3725             continue;
3726             }
3727         else if (!isRegularFile(fullChild))
3728             {
3729             error("unknown file:%s", childName.c_str());
3730             return false;
3731             }
3733        //all done!
3734         res.push_back(childName);
3736         }
3737     closedir(dir);
3739     return true;
3743 bool MakeBase::listFiles(MakeBase &propRef, FileSet &fileSet)
3745     String baseDir = propRef.resolve(fileSet.getDirectory());
3746     std::vector<String> fileList;
3747     if (!listFiles(baseDir, "", fileList))
3748         return false;
3750     std::vector<String> includes = fileSet.getIncludes();
3751     std::vector<String> excludes = fileSet.getExcludes();
3753     std::vector<String> incs;
3754     std::vector<String>::iterator iter;
3756     std::sort(fileList.begin(), fileList.end());
3758     //If there are <includes>, then add files to the output
3759     //in the order of the include list
3760     if (includes.size()==0)
3761         incs = fileList;
3762     else
3763         {
3764         for (iter = includes.begin() ; iter != includes.end() ; iter++)
3765             {
3766             String pattern = *iter;
3767             std::vector<String>::iterator siter;
3768             for (siter = fileList.begin() ; siter != fileList.end() ; siter++)
3769                 {
3770                 String s = *siter;
3771                 if (regexMatch(s, pattern))
3772                     {
3773                     //trace("INCLUDED:%s", s.c_str());
3774                     incs.push_back(s);
3775                     }
3776                 }
3777             }
3778         }
3780     //Now trim off the <excludes>
3781     std::vector<String> res;
3782     for (iter = incs.begin() ; iter != incs.end() ; iter++)
3783         {
3784         String s = *iter;
3785         bool skipme = false;
3786         std::vector<String>::iterator siter;
3787         for (siter = excludes.begin() ; siter != excludes.end() ; siter++)
3788             {
3789             String pattern = *siter;
3790             if (regexMatch(s, pattern))
3791                 {
3792                 //trace("EXCLUDED:%s", s.c_str());
3793                 skipme = true;
3794                 break;
3795                 }
3796             }
3797         if (!skipme)
3798             res.push_back(s);
3799         }
3800         
3801     fileSet.setFiles(res);
3803     return true;
3810 bool MakeBase::getSubstitutions(const String &str, String &result)
3812     String s = trim(str);
3813     int len = (int)s.size();
3814     String val;
3815     for (int i=0 ; i<len ; i++)
3816         {
3817         char ch = s[i];
3818         if (ch == '$' && s[i+1] == '{')
3819             {
3820             String varname;
3821             int j = i+2;
3822             for ( ; j<len ; j++)
3823                 {
3824                 ch = s[j];
3825                 if (ch == '$' && s[j+1] == '{')
3826                     {
3827                     error("attribute %s cannot have nested variable references",
3828                            s.c_str());
3829                     return false;
3830                     }
3831                 else if (ch == '}')
3832                     {
3833                     std::map<String, String>::iterator iter;
3834                     iter = properties.find(trim(varname));
3835                     if (iter != properties.end())
3836                         {
3837                         val.append(iter->second);
3838                         }
3839                     else
3840                         {
3841                         error("property ${%s} not found", varname.c_str());
3842                         return false;
3843                         }
3844                     break;
3845                     }
3846                 else
3847                     {
3848                     varname.push_back(ch);
3849                     }
3850                 }
3851             i = j;
3852             }
3853         else
3854             {
3855             val.push_back(ch);
3856             }
3857         }
3858     result = val;
3859     return true;
3863 bool MakeBase::getAttribute(Element *elem, const String &name,
3864                                     String &result)
3866     String s = elem->getAttribute(name);
3867     return getSubstitutions(s, result);
3871 bool MakeBase::getValue(Element *elem, String &result)
3873     String s = elem->getValue();
3874     //Replace all runs of whitespace with a single space
3875     return getSubstitutions(s, result);
3879 /**
3880  * Turn 'true' and 'false' into boolean values
3881  */             
3882 bool MakeBase::getBool(const String &str, bool &val)
3884     if (str == "true")
3885         val = true;
3886     else if (str == "false")
3887         val = false;
3888     else
3889         {
3890         error("expected 'true' or 'false'.  found '%s'", str.c_str());
3891         return false;
3892         }
3893     return true;
3899 /**
3900  * Parse a <patternset> entry
3901  */  
3902 bool MakeBase::parsePatternSet(Element *elem,
3903                           MakeBase &propRef,
3904                           std::vector<String> &includes,
3905                           std::vector<String> &excludes
3906                           )
3908     std::vector<Element *> children  = elem->getChildren();
3909     for (unsigned int i=0 ; i<children.size() ; i++)
3910         {
3911         Element *child = children[i];
3912         String tagName = child->getName();
3913         if (tagName == "exclude")
3914             {
3915             String fname;
3916             if (!propRef.getAttribute(child, "name", fname))
3917                 return false;
3918             //trace("EXCLUDE: %s", fname.c_str());
3919             excludes.push_back(fname);
3920             }
3921         else if (tagName == "include")
3922             {
3923             String fname;
3924             if (!propRef.getAttribute(child, "name", fname))
3925                 return false;
3926             //trace("INCLUDE: %s", fname.c_str());
3927             includes.push_back(fname);
3928             }
3929         }
3931     return true;
3937 /**
3938  * Parse a <fileset> entry, and determine which files
3939  * should be included
3940  */  
3941 bool MakeBase::parseFileSet(Element *elem,
3942                           MakeBase &propRef,
3943                           FileSet &fileSet)
3945     String name = elem->getName();
3946     if (name != "fileset")
3947         {
3948         error("expected <fileset>");
3949         return false;
3950         }
3953     std::vector<String> includes;
3954     std::vector<String> excludes;
3956     //A fileset has one implied patternset
3957     if (!parsePatternSet(elem, propRef, includes, excludes))
3958         {
3959         return false;
3960         }
3961     //Look for child tags, including more patternsets
3962     std::vector<Element *> children  = elem->getChildren();
3963     for (unsigned int i=0 ; i<children.size() ; i++)
3964         {
3965         Element *child = children[i];
3966         String tagName = child->getName();
3967         if (tagName == "patternset")
3968             {
3969             if (!parsePatternSet(child, propRef, includes, excludes))
3970                 {
3971                 return false;
3972                 }
3973             }
3974         }
3976     String dir;
3977     //Now do the stuff
3978     //Get the base directory for reading file names
3979     if (!propRef.getAttribute(elem, "dir", dir))
3980         return false;
3982     fileSet.setDirectory(dir);
3983     fileSet.setIncludes(includes);
3984     fileSet.setExcludes(excludes);
3985     
3986     /*
3987     std::vector<String> fileList;
3988     if (dir.size() > 0)
3989         {
3990         String baseDir = propRef.resolve(dir);
3991         if (!listFiles(baseDir, "", includes, excludes, fileList))
3992             return false;
3993         }
3994     std::sort(fileList.begin(), fileList.end());
3995     result = fileList;
3996     */
3998     
3999     /*
4000     for (unsigned int i=0 ; i<result.size() ; i++)
4001         {
4002         trace("RES:%s", result[i].c_str());
4003         }
4004     */
4006     
4007     return true;
4012 /**
4013  * Create a directory, making intermediate dirs
4014  * if necessary
4015  */                  
4016 bool MakeBase::createDirectory(const String &dirname)
4018     //trace("## createDirectory: %s", dirname.c_str());
4019     //## first check if it exists
4020     struct stat finfo;
4021     String nativeDir = getNativePath(dirname);
4022     char *cnative = (char *) nativeDir.c_str();
4023 #ifdef __WIN32__
4024     if (strlen(cnative)==2 && cnative[1]==':')
4025         return true;
4026 #endif
4027     if (stat(cnative, &finfo)==0)
4028         {
4029         if (!S_ISDIR(finfo.st_mode))
4030             {
4031             error("mkdir: file %s exists but is not a directory",
4032                   cnative);
4033             return false;
4034             }
4035         else //exists
4036             {
4037             return true;
4038             }
4039         }
4041     //## 2: pull off the last path segment, if any,
4042     //## to make the dir 'above' this one, if necessary
4043     unsigned int pos = dirname.find_last_of('/');
4044     if (pos>0 && pos != dirname.npos)
4045         {
4046         String subpath = dirname.substr(0, pos);
4047         //A letter root (c:) ?
4048         if (!createDirectory(subpath))
4049             return false;
4050         }
4051         
4052     //## 3: now make
4053 #ifdef __WIN32__
4054     if (mkdir(cnative)<0)
4055 #else
4056     if (mkdir(cnative, S_IRWXU | S_IRWXG | S_IRWXO)<0)
4057 #endif
4058         {
4059         error("cannot make directory '%s' : %s",
4060                  cnative, strerror(errno));
4061         return false;
4062         }
4063         
4064     return true;
4068 /**
4069  * Remove a directory recursively
4070  */ 
4071 bool MakeBase::removeDirectory(const String &dirName)
4073     char *dname = (char *)dirName.c_str();
4075     DIR *dir = opendir(dname);
4076     if (!dir)
4077         {
4078         //# Let this fail nicely.
4079         return true;
4080         //error("error opening directory %s : %s", dname, strerror(errno));
4081         //return false;
4082         }
4083     
4084     while (true)
4085         {
4086         struct dirent *de = readdir(dir);
4087         if (!de)
4088             break;
4090         //Get the directory member name
4091         String s = de->d_name;
4092         if (s.size() == 0 || s[0] == '.')
4093             continue;
4094         String childName;
4095         if (dirName.size() > 0)
4096             {
4097             childName.append(dirName);
4098             childName.append("/");
4099             }
4100         childName.append(s);
4103         struct stat finfo;
4104         String childNative = getNativePath(childName);
4105         char *cnative = (char *)childNative.c_str();
4106         if (stat(cnative, &finfo)<0)
4107             {
4108             error("cannot stat file:%s", cnative);
4109             }
4110         else if (S_ISDIR(finfo.st_mode))
4111             {
4112             //trace("DEL dir: %s", childName.c_str());
4113             if (!removeDirectory(childName))
4114                 {
4115                 return false;
4116                 }
4117             }
4118         else if (!S_ISREG(finfo.st_mode))
4119             {
4120             //trace("not regular: %s", cnative);
4121             }
4122         else
4123             {
4124             //trace("DEL file: %s", childName.c_str());
4125             if (remove(cnative)<0)
4126                 {
4127                 error("error deleting %s : %s",
4128                      cnative, strerror(errno));
4129                 return false;
4130                 }
4131             }
4132         }
4133     closedir(dir);
4135     //Now delete the directory
4136     String native = getNativePath(dirName);
4137     if (rmdir(native.c_str())<0)
4138         {
4139         error("could not delete directory %s : %s",
4140             native.c_str() , strerror(errno));
4141         return false;
4142         }
4144     return true;
4145     
4149 /**
4150  * Copy a file from one name to another. Perform only if needed
4151  */ 
4152 bool MakeBase::copyFile(const String &srcFile, const String &destFile)
4154     //# 1 Check up-to-date times
4155     String srcNative = getNativePath(srcFile);
4156     struct stat srcinfo;
4157     if (stat(srcNative.c_str(), &srcinfo)<0)
4158         {
4159         error("source file %s for copy does not exist",
4160                  srcNative.c_str());
4161         return false;
4162         }
4164     String destNative = getNativePath(destFile);
4165     struct stat destinfo;
4166     if (stat(destNative.c_str(), &destinfo)==0)
4167         {
4168         if (destinfo.st_mtime >= srcinfo.st_mtime)
4169             return true;
4170         }
4171         
4172     //# 2 prepare a destination directory if necessary
4173     unsigned int pos = destFile.find_last_of('/');
4174     if (pos != destFile.npos)
4175         {
4176         String subpath = destFile.substr(0, pos);
4177         if (!createDirectory(subpath))
4178             return false;
4179         }
4181     //# 3 do the data copy
4182 #ifndef __WIN32__
4184     FILE *srcf = fopen(srcNative.c_str(), "rb");
4185     if (!srcf)
4186         {
4187         error("copyFile cannot open '%s' for reading", srcNative.c_str());
4188         return false;
4189         }
4190     FILE *destf = fopen(destNative.c_str(), "wb");
4191     if (!destf)
4192         {
4193         error("copyFile cannot open %s for writing", srcNative.c_str());
4194         return false;
4195         }
4197     while (!feof(srcf))
4198         {
4199         int ch = fgetc(srcf);
4200         if (ch<0)
4201             break;
4202         fputc(ch, destf);
4203         }
4205     fclose(destf);
4206     fclose(srcf);
4208 #else
4209     
4210     if (!CopyFile(srcNative.c_str(), destNative.c_str(), false))
4211         {
4212         error("copyFile from %s to %s failed",
4213              srcNative.c_str(), destNative.c_str());
4214         return false;
4215         }
4216         
4217 #endif /* __WIN32__ */
4220     return true;
4225 /**
4226  * Tests if the file exists and is a regular file
4227  */ 
4228 bool MakeBase::isRegularFile(const String &fileName)
4230     String native = getNativePath(fileName);
4231     struct stat finfo;
4232     
4233     //Exists?
4234     if (stat(native.c_str(), &finfo)<0)
4235         return false;
4238     //check the file mode
4239     if (!S_ISREG(finfo.st_mode))
4240         return false;
4242     return true;
4245 /**
4246  * Tests if the file exists and is a directory
4247  */ 
4248 bool MakeBase::isDirectory(const String &fileName)
4250     String native = getNativePath(fileName);
4251     struct stat finfo;
4252     
4253     //Exists?
4254     if (stat(native.c_str(), &finfo)<0)
4255         return false;
4258     //check the file mode
4259     if (!S_ISDIR(finfo.st_mode))
4260         return false;
4262     return true;
4267 /**
4268  * Tests is the modification of fileA is newer than fileB
4269  */ 
4270 bool MakeBase::isNewerThan(const String &fileA, const String &fileB)
4272     //trace("isNewerThan:'%s' , '%s'", fileA.c_str(), fileB.c_str());
4273     String nativeA = getNativePath(fileA);
4274     struct stat infoA;
4275     //IF source does not exist, NOT newer
4276     if (stat(nativeA.c_str(), &infoA)<0)
4277         {
4278         return false;
4279         }
4281     String nativeB = getNativePath(fileB);
4282     struct stat infoB;
4283     //IF dest does not exist, YES, newer
4284     if (stat(nativeB.c_str(), &infoB)<0)
4285         {
4286         return true;
4287         }
4289     //check the actual times
4290     if (infoA.st_mtime > infoB.st_mtime)
4291         {
4292         return true;
4293         }
4295     return false;
4299 //########################################################################
4300 //# P K G    C O N F I G
4301 //########################################################################
4303 /**
4304  *
4305  */
4306 class PkgConfig : public MakeBase
4309 public:
4311     /**
4312      *
4313      */
4314     PkgConfig()
4315         { init(); }
4317     /**
4318      *
4319      */
4320     PkgConfig(const String &namearg)
4321         { init(); name = namearg; }
4323     /**
4324      *
4325      */
4326     PkgConfig(const PkgConfig &other)
4327         { assign(other); }
4329     /**
4330      *
4331      */
4332     PkgConfig &operator=(const PkgConfig &other)
4333         { assign(other); return *this; }
4335     /**
4336      *
4337      */
4338     virtual ~PkgConfig()
4339         { }
4341     /**
4342      *
4343      */
4344     virtual String getName()
4345         { return name; }
4347     /**
4348      *
4349      */
4350     virtual String getDescription()
4351         { return description; }
4353     /**
4354      *
4355      */
4356     virtual String getCflags()
4357         { return cflags; }
4359     /**
4360      *
4361      */
4362     virtual String getLibs()
4363         { return libs; }
4365     /**
4366      *
4367      */
4368     virtual String getVersion()
4369         { return version; }
4371     /**
4372      *
4373      */
4374     virtual int getMajorVersion()
4375         { return majorVersion; }
4377     /**
4378      *
4379      */
4380     virtual int getMinorVersion()
4381         { return minorVersion; }
4383     /**
4384      *
4385      */
4386     virtual int getMicroVersion()
4387         { return microVersion; }
4389     /**
4390      *
4391      */
4392     virtual std::map<String, String> &getAttributes()
4393         { return attrs; }
4395     /**
4396      *
4397      */
4398     virtual std::vector<String> &getRequireList()
4399         { return requireList; }
4401     virtual bool readFile(const String &fileName);
4403 private:
4405     void init()
4406         {
4407         name         = "";
4408         description  = "";
4409         cflags       = "";
4410         libs         = "";
4411         requires     = "";
4412         version      = "";
4413         majorVersion = 0;
4414         minorVersion = 0;
4415         microVersion = 0;
4416         fileName     = "";
4417         attrs.clear();
4418         requireList.clear();
4419         }
4421     void assign(const PkgConfig &other)
4422         {
4423         name         = other.name;
4424         description  = other.description;
4425         cflags       = other.cflags;
4426         libs         = other.libs;
4427         requires     = other.requires;
4428         version      = other.version;
4429         majorVersion = other.majorVersion;
4430         minorVersion = other.minorVersion;
4431         microVersion = other.microVersion;
4432         fileName     = other.fileName;
4433         attrs        = other.attrs;
4434         requireList  = other.requireList;
4435         }
4439     int get(int pos);
4441     int skipwhite(int pos);
4443     int getword(int pos, String &ret);
4445     void parseRequires();
4447     void parseVersion();
4449     bool parse(const String &buf);
4451     void dumpAttrs();
4453     String name;
4455     String description;
4457     String cflags;
4459     String libs;
4461     String requires;
4463     String version;
4465     int majorVersion;
4467     int minorVersion;
4469     int microVersion;
4471     String fileName;
4473     std::map<String, String> attrs;
4475     std::vector<String> requireList;
4477     char *parsebuf;
4478     int parselen;
4479 };
4482 /**
4483  * Get a character from the buffer at pos.  If out of range,
4484  * return -1 for safety
4485  */
4486 int PkgConfig::get(int pos)
4488     if (pos>parselen)
4489         return -1;
4490     return parsebuf[pos];
4495 /**
4496  *  Skip over all whitespace characters beginning at pos.  Return
4497  *  the position of the first non-whitespace character.
4498  */
4499 int PkgConfig::skipwhite(int pos)
4501     while (pos < parselen)
4502         {
4503         int ch = get(pos);
4504         if (ch < 0)
4505             break;
4506         if (!isspace(ch))
4507             break;
4508         pos++;
4509         }
4510     return pos;
4514 /**
4515  *  Parse the buffer beginning at pos, for a word.  Fill
4516  *  'ret' with the result.  Return the position after the
4517  *  word.
4518  */
4519 int PkgConfig::getword(int pos, String &ret)
4521     while (pos < parselen)
4522         {
4523         int ch = get(pos);
4524         if (ch < 0)
4525             break;
4526         if (!isalnum(ch) && ch != '_' && ch != '-'&& ch != '.')
4527             break;
4528         ret.push_back((char)ch);
4529         pos++;
4530         }
4531     return pos;
4534 void PkgConfig::parseRequires()
4536     if (requires.size() == 0)
4537         return;
4538     parsebuf = (char *)requires.c_str();
4539     parselen = requires.size();
4540     int pos = 0;
4541     while (pos < parselen)
4542         {
4543         pos = skipwhite(pos);
4544         String val;
4545         int pos2 = getword(pos, val);
4546         if (pos2 == pos)
4547             break;
4548         pos = pos2;
4549         //trace("val %s", val.c_str());
4550         requireList.push_back(val);
4551         }
4554 static int getint(const String str)
4556     char *s = (char *)str.c_str();
4557     char *ends = NULL;
4558     long val = strtol(s, &ends, 10);
4559     if (ends == s)
4560         return 0L;
4561     else
4562         return val;
4565 void PkgConfig::parseVersion()
4567     if (version.size() == 0)
4568         return;
4569     String s1, s2, s3;
4570     unsigned int pos = 0;
4571     unsigned int pos2 = version.find('.', pos);
4572     if (pos2 == version.npos)
4573         {
4574         s1 = version;
4575         }
4576     else
4577         {
4578         s1 = version.substr(pos, pos2-pos);
4579         pos = pos2;
4580         pos++;
4581         if (pos < version.size())
4582             {
4583             pos2 = version.find('.', pos);
4584             if (pos2 == version.npos)
4585                 {
4586                 s2 = version.substr(pos, version.size()-pos);
4587                 }
4588             else
4589                 {
4590                 s2 = version.substr(pos, pos2-pos);
4591                 pos = pos2;
4592                 pos++;
4593                 if (pos < version.size())
4594                     s3 = version.substr(pos, pos2-pos);
4595                 }
4596             }
4597         }
4599     majorVersion = getint(s1);
4600     minorVersion = getint(s2);
4601     microVersion = getint(s3);
4602     //trace("version:%d.%d.%d", majorVersion,
4603     //          minorVersion, microVersion );
4607 bool PkgConfig::parse(const String &buf)
4609     init();
4611     parsebuf = (char *)buf.c_str();
4612     parselen = buf.size();
4613     int pos = 0;
4616     while (pos < parselen)
4617         {
4618         String attrName;
4619         pos = skipwhite(pos);
4620         int ch = get(pos);
4621         if (ch == '#')
4622             {
4623             //comment.  eat the rest of the line
4624             while (pos < parselen)
4625                 {
4626                 ch = get(pos);
4627                 if (ch == '\n' || ch < 0)
4628                     break;
4629                 pos++;
4630                 }
4631             continue;
4632             }
4633         pos = getword(pos, attrName);
4634         if (attrName.size() == 0)
4635             continue;
4636         pos = skipwhite(pos);
4637         ch = get(pos);
4638         if (ch != ':' && ch != '=')
4639             {
4640             error("expected ':' or '='");
4641             return false;
4642             }
4643         pos++;
4644         pos = skipwhite(pos);
4645         String attrVal;
4646         while (pos < parselen)
4647             {
4648             ch = get(pos);
4649             if (ch == '\n' || ch < 0)
4650                 break;
4651             else if (ch == '$' && get(pos+1) == '{')
4652                 {
4653                 //#  this is a ${substitution}
4654                 pos += 2;
4655                 String subName;
4656                 while (pos < parselen)
4657                     {
4658                     ch = get(pos);
4659                     if (ch < 0)
4660                         {
4661                         error("unterminated substitution");
4662                         return false;
4663                         }
4664                     else if (ch == '}')
4665                         break;
4666                     else
4667                         subName.push_back((char)ch);
4668                     pos++;
4669                     }
4670                 //trace("subName:%s", subName.c_str());
4671                 String subVal = attrs[subName];
4672                 //trace("subVal:%s", subVal.c_str());
4673                 attrVal.append(subVal);
4674                 }
4675             else
4676                 attrVal.push_back((char)ch);
4677             pos++;
4678             }
4680         attrVal = trim(attrVal);
4681         attrs[attrName] = attrVal;
4683         if (attrName == "Name")
4684             name = attrVal;
4685         else if (attrName == "Description")
4686             description = attrVal;
4687         else if (attrName == "Cflags")
4688             cflags = attrVal;
4689         else if (attrName == "Libs")
4690             libs = attrVal;
4691         else if (attrName == "Requires")
4692             requires = attrVal;
4693         else if (attrName == "Version")
4694             version = attrVal;
4696         //trace("name:'%s'  value:'%s'",
4697         //      attrName.c_str(), attrVal.c_str());
4698         }
4701     parseRequires();
4702     parseVersion();
4704     return true;
4707 void PkgConfig::dumpAttrs()
4709     //trace("### PkgConfig attributes for %s", fileName.c_str());
4710     std::map<String, String>::iterator iter;
4711     for (iter=attrs.begin() ; iter!=attrs.end() ; iter++)
4712         {
4713         trace("   %s = %s", iter->first.c_str(), iter->second.c_str());
4714         }
4718 bool PkgConfig::readFile(const String &fileNameArg)
4720     fileName = fileNameArg;
4722     FILE *f = fopen(fileName.c_str(), "r");
4723     if (!f)
4724         {
4725         error("cannot open file '%s' for reading", fileName.c_str());
4726         return false;
4727         }
4728     String buf;
4729     while (true)
4730         {
4731         int ch = fgetc(f);
4732         if (ch < 0)
4733             break;
4734         buf.push_back((char)ch);
4735         }
4736     fclose(f);
4738     //trace("####### File:\n%s", buf.c_str());
4739     if (!parse(buf))
4740         {
4741         return false;
4742         }
4744     dumpAttrs();
4746     return true;
4753 //########################################################################
4754 //# D E P T O O L
4755 //########################################################################
4759 /**
4760  *  Class which holds information for each file.
4761  */
4762 class FileRec
4764 public:
4766     typedef enum
4767         {
4768         UNKNOWN,
4769         CFILE,
4770         HFILE,
4771         OFILE
4772         } FileType;
4774     /**
4775      *  Constructor
4776      */
4777     FileRec()
4778         { init(); type = UNKNOWN; }
4780     /**
4781      *  Copy constructor
4782      */
4783     FileRec(const FileRec &other)
4784         { init(); assign(other); }
4785     /**
4786      *  Constructor
4787      */
4788     FileRec(int typeVal)
4789         { init(); type = typeVal; }
4790     /**
4791      *  Assignment operator
4792      */
4793     FileRec &operator=(const FileRec &other)
4794         { init(); assign(other); return *this; }
4797     /**
4798      *  Destructor
4799      */
4800     ~FileRec()
4801         {}
4803     /**
4804      *  Directory part of the file name
4805      */
4806     String path;
4808     /**
4809      *  Base name, sans directory and suffix
4810      */
4811     String baseName;
4813     /**
4814      *  File extension, such as cpp or h
4815      */
4816     String suffix;
4818     /**
4819      *  Type of file: CFILE, HFILE, OFILE
4820      */
4821     int type;
4823     /**
4824      * Used to list files ref'd by this one
4825      */
4826     std::map<String, FileRec *> files;
4829 private:
4831     void init()
4832         {
4833         }
4835     void assign(const FileRec &other)
4836         {
4837         type     = other.type;
4838         baseName = other.baseName;
4839         suffix   = other.suffix;
4840         files    = other.files;
4841         }
4843 };
4847 /**
4848  *  Simpler dependency record
4849  */
4850 class DepRec
4852 public:
4854     /**
4855      *  Constructor
4856      */
4857     DepRec()
4858         {init();}
4860     /**
4861      *  Copy constructor
4862      */
4863     DepRec(const DepRec &other)
4864         {init(); assign(other);}
4865     /**
4866      *  Constructor
4867      */
4868     DepRec(const String &fname)
4869         {init(); name = fname; }
4870     /**
4871      *  Assignment operator
4872      */
4873     DepRec &operator=(const DepRec &other)
4874         {init(); assign(other); return *this;}
4877     /**
4878      *  Destructor
4879      */
4880     ~DepRec()
4881         {}
4883     /**
4884      *  Directory part of the file name
4885      */
4886     String path;
4888     /**
4889      *  Base name, without the path and suffix
4890      */
4891     String name;
4893     /**
4894      *  Suffix of the source
4895      */
4896     String suffix;
4899     /**
4900      * Used to list files ref'd by this one
4901      */
4902     std::vector<String> files;
4905 private:
4907     void init()
4908         {
4909         }
4911     void assign(const DepRec &other)
4912         {
4913         path     = other.path;
4914         name     = other.name;
4915         suffix   = other.suffix;
4916         files    = other.files; //avoid recursion
4917         }
4919 };
4922 class DepTool : public MakeBase
4924 public:
4926     /**
4927      *  Constructor
4928      */
4929     DepTool()
4930         { init(); }
4932     /**
4933      *  Copy constructor
4934      */
4935     DepTool(const DepTool &other)
4936         { init(); assign(other); }
4938     /**
4939      *  Assignment operator
4940      */
4941     DepTool &operator=(const DepTool &other)
4942         { init(); assign(other); return *this; }
4945     /**
4946      *  Destructor
4947      */
4948     ~DepTool()
4949         {}
4952     /**
4953      *  Reset this section of code
4954      */
4955     virtual void init();
4956     
4957     /**
4958      *  Reset this section of code
4959      */
4960     virtual void assign(const DepTool &other)
4961         {
4962         }
4963     
4964     /**
4965      *  Sets the source directory which will be scanned
4966      */
4967     virtual void setSourceDirectory(const String &val)
4968         { sourceDir = val; }
4970     /**
4971      *  Returns the source directory which will be scanned
4972      */
4973     virtual String getSourceDirectory()
4974         { return sourceDir; }
4976     /**
4977      *  Sets the list of files within the directory to analyze
4978      */
4979     virtual void setFileList(const std::vector<String> &list)
4980         { fileList = list; }
4982     /**
4983      * Creates the list of all file names which will be
4984      * candidates for further processing.  Reads make.exclude
4985      * to see which files for directories to leave out.
4986      */
4987     virtual bool createFileList();
4990     /**
4991      *  Generates the forward dependency list
4992      */
4993     virtual bool generateDependencies();
4996     /**
4997      *  Generates the forward dependency list, saving the file
4998      */
4999     virtual bool generateDependencies(const String &);
5002     /**
5003      *  Load a dependency file
5004      */
5005     std::vector<DepRec> loadDepFile(const String &fileName);
5007     /**
5008      *  Load a dependency file, generating one if necessary
5009      */
5010     std::vector<DepRec> getDepFile(const String &fileName,
5011               bool forceRefresh);
5013     /**
5014      *  Save a dependency file
5015      */
5016     bool saveDepFile(const String &fileName);
5019 private:
5022     /**
5023      *
5024      */
5025     void parseName(const String &fullname,
5026                    String &path,
5027                    String &basename,
5028                    String &suffix);
5030     /**
5031      *
5032      */
5033     int get(int pos);
5035     /**
5036      *
5037      */
5038     int skipwhite(int pos);
5040     /**
5041      *
5042      */
5043     int getword(int pos, String &ret);
5045     /**
5046      *
5047      */
5048     bool sequ(int pos, char *key);
5050     /**
5051      *
5052      */
5053     bool addIncludeFile(FileRec *frec, const String &fname);
5055     /**
5056      *
5057      */
5058     bool scanFile(const String &fname, FileRec *frec);
5060     /**
5061      *
5062      */
5063     bool processDependency(FileRec *ofile, FileRec *include);
5065     /**
5066      *
5067      */
5068     String sourceDir;
5070     /**
5071      *
5072      */
5073     std::vector<String> fileList;
5075     /**
5076      *
5077      */
5078     std::vector<String> directories;
5080     /**
5081      * A list of all files which will be processed for
5082      * dependencies.
5083      */
5084     std::map<String, FileRec *> allFiles;
5086     /**
5087      * The list of .o files, and the
5088      * dependencies upon them.
5089      */
5090     std::map<String, FileRec *> oFiles;
5092     int depFileSize;
5093     char *depFileBuf;
5095     static const int readBufSize = 8192;
5096     char readBuf[8193];//byte larger
5098 };
5104 /**
5105  *  Clean up after processing.  Called by the destructor, but should
5106  *  also be called before the object is reused.
5107  */
5108 void DepTool::init()
5110     sourceDir = ".";
5112     fileList.clear();
5113     directories.clear();
5114     
5115     //clear output file list
5116     std::map<String, FileRec *>::iterator iter;
5117     for (iter=oFiles.begin(); iter!=oFiles.end() ; iter++)
5118         delete iter->second;
5119     oFiles.clear();
5121     //allFiles actually contains the master copies. delete them
5122     for (iter= allFiles.begin(); iter!=allFiles.end() ; iter++)
5123         delete iter->second;
5124     allFiles.clear(); 
5131 /**
5132  *  Parse a full path name into path, base name, and suffix
5133  */
5134 void DepTool::parseName(const String &fullname,
5135                         String &path,
5136                         String &basename,
5137                         String &suffix)
5139     if (fullname.size() < 2)
5140         return;
5142     unsigned int pos = fullname.find_last_of('/');
5143     if (pos != fullname.npos && pos<fullname.size()-1)
5144         {
5145         path = fullname.substr(0, pos);
5146         pos++;
5147         basename = fullname.substr(pos, fullname.size()-pos);
5148         }
5149     else
5150         {
5151         path = "";
5152         basename = fullname;
5153         }
5155     pos = basename.find_last_of('.');
5156     if (pos != basename.npos && pos<basename.size()-1)
5157         {
5158         suffix   = basename.substr(pos+1, basename.size()-pos-1);
5159         basename = basename.substr(0, pos);
5160         }
5162     //trace("parsename:%s %s %s", path.c_str(),
5163     //        basename.c_str(), suffix.c_str()); 
5168 /**
5169  *  Generate our internal file list.
5170  */
5171 bool DepTool::createFileList()
5174     for (unsigned int i=0 ; i<fileList.size() ; i++)
5175         {
5176         String fileName = fileList[i];
5177         //trace("## FileName:%s", fileName.c_str());
5178         String path;
5179         String basename;
5180         String sfx;
5181         parseName(fileName, path, basename, sfx);
5182         if (sfx == "cpp" || sfx == "c" || sfx == "cxx"   ||
5183             sfx == "cc" || sfx == "CC")
5184             {
5185             FileRec *fe         = new FileRec(FileRec::CFILE);
5186             fe->path            = path;
5187             fe->baseName        = basename;
5188             fe->suffix          = sfx;
5189             allFiles[fileName]  = fe;
5190             }
5191         else if (sfx == "h"   ||  sfx == "hh"  ||
5192                  sfx == "hpp" ||  sfx == "hxx")
5193             {
5194             FileRec *fe         = new FileRec(FileRec::HFILE);
5195             fe->path            = path;
5196             fe->baseName        = basename;
5197             fe->suffix          = sfx;
5198             allFiles[fileName]  = fe;
5199             }
5200         }
5202     if (!listDirectories(sourceDir, "", directories))
5203         return false;
5204         
5205     return true;
5212 /**
5213  * Get a character from the buffer at pos.  If out of range,
5214  * return -1 for safety
5215  */
5216 int DepTool::get(int pos)
5218     if (pos>depFileSize)
5219         return -1;
5220     return depFileBuf[pos];
5225 /**
5226  *  Skip over all whitespace characters beginning at pos.  Return
5227  *  the position of the first non-whitespace character.
5228  */
5229 int DepTool::skipwhite(int pos)
5231     while (pos < depFileSize)
5232         {
5233         int ch = get(pos);
5234         if (ch < 0)
5235             break;
5236         if (!isspace(ch))
5237             break;
5238         pos++;
5239         }
5240     return pos;
5244 /**
5245  *  Parse the buffer beginning at pos, for a word.  Fill
5246  *  'ret' with the result.  Return the position after the
5247  *  word.
5248  */
5249 int DepTool::getword(int pos, String &ret)
5251     while (pos < depFileSize)
5252         {
5253         int ch = get(pos);
5254         if (ch < 0)
5255             break;
5256         if (isspace(ch))
5257             break;
5258         ret.push_back((char)ch);
5259         pos++;
5260         }
5261     return pos;
5264 /**
5265  * Return whether the sequence of characters in the buffer
5266  * beginning at pos match the key,  for the length of the key
5267  */
5268 bool DepTool::sequ(int pos, char *key)
5270     while (*key)
5271         {
5272         if (*key != get(pos))
5273             return false;
5274         key++; pos++;
5275         }
5276     return true;
5281 /**
5282  *  Add an include file name to a file record.  If the name
5283  *  is not found in allFiles explicitly, try prepending include
5284  *  directory names to it and try again.
5285  */
5286 bool DepTool::addIncludeFile(FileRec *frec, const String &iname)
5288     //# if the name is an exact match to a path name
5289     //# in allFiles, like "myinc.h"
5290     std::map<String, FileRec *>::iterator iter =
5291            allFiles.find(iname);
5292     if (iter != allFiles.end()) //already exists
5293         {
5294          //h file in same dir
5295         FileRec *other = iter->second;
5296         //trace("local: '%s'", iname.c_str());
5297         frec->files[iname] = other;
5298         return true;
5299         }
5300     else 
5301         {
5302         //## Ok, it was not found directly
5303         //look in other dirs
5304         std::vector<String>::iterator diter;
5305         for (diter=directories.begin() ;
5306              diter!=directories.end() ; diter++)
5307             {
5308             String dfname = *diter;
5309             dfname.append("/");
5310             dfname.append(iname);
5311             URI fullPathURI(dfname);  //normalize path name
5312             String fullPath = fullPathURI.getPath();
5313             if (fullPath[0] == '/')
5314                 fullPath = fullPath.substr(1);
5315             //trace("Normalized %s to %s", dfname.c_str(), fullPath.c_str());
5316             iter = allFiles.find(fullPath);
5317             if (iter != allFiles.end())
5318                 {
5319                 FileRec *other = iter->second;
5320                 //trace("other: '%s'", iname.c_str());
5321                 frec->files[fullPath] = other;
5322                 return true;
5323                 }
5324             }
5325         }
5326     return true;
5331 /**
5332  *  Lightly parse a file to find the #include directives.  Do
5333  *  a bit of state machine stuff to make sure that the directive
5334  *  is valid.  (Like not in a comment).
5335  */
5336 bool DepTool::scanFile(const String &fname, FileRec *frec)
5338     String fileName;
5339     if (sourceDir.size() > 0)
5340         {
5341         fileName.append(sourceDir);
5342         fileName.append("/");
5343         }
5344     fileName.append(fname);
5345     String nativeName = getNativePath(fileName);
5346     FILE *f = fopen(nativeName.c_str(), "r");
5347     if (!f)
5348         {
5349         error("Could not open '%s' for reading", fname.c_str());
5350         return false;
5351         }
5352     String buf;
5353     while (!feof(f))
5354         {
5355         int len = fread(readBuf, 1, readBufSize, f);
5356         readBuf[len] = '\0';
5357         buf.append(readBuf);
5358         }
5359     fclose(f);
5361     depFileSize = buf.size();
5362     depFileBuf  = (char *)buf.c_str();
5363     int pos = 0;
5366     while (pos < depFileSize)
5367         {
5368         //trace("p:%c", get(pos));
5370         //# Block comment
5371         if (get(pos) == '/' && get(pos+1) == '*')
5372             {
5373             pos += 2;
5374             while (pos < depFileSize)
5375                 {
5376                 if (get(pos) == '*' && get(pos+1) == '/')
5377                     {
5378                     pos += 2;
5379                     break;
5380                     }
5381                 else
5382                     pos++;
5383                 }
5384             }
5385         //# Line comment
5386         else if (get(pos) == '/' && get(pos+1) == '/')
5387             {
5388             pos += 2;
5389             while (pos < depFileSize)
5390                 {
5391                 if (get(pos) == '\n')
5392                     {
5393                     pos++;
5394                     break;
5395                     }
5396                 else
5397                     pos++;
5398                 }
5399             }
5400         //# #include! yaay
5401         else if (sequ(pos, "#include"))
5402             {
5403             pos += 8;
5404             pos = skipwhite(pos);
5405             String iname;
5406             pos = getword(pos, iname);
5407             if (iname.size()>2)
5408                 {
5409                 iname = iname.substr(1, iname.size()-2);
5410                 addIncludeFile(frec, iname);
5411                 }
5412             }
5413         else
5414             {
5415             pos++;
5416             }
5417         }
5419     return true;
5424 /**
5425  *  Recursively check include lists to find all files in allFiles to which
5426  *  a given file is dependent.
5427  */
5428 bool DepTool::processDependency(FileRec *ofile, FileRec *include)
5430     std::map<String, FileRec *>::iterator iter;
5431     for (iter=include->files.begin() ; iter!=include->files.end() ; iter++)
5432         {
5433         String fname  = iter->first;
5434         if (ofile->files.find(fname) != ofile->files.end())
5435             {
5436             //trace("file '%s' already seen", fname.c_str());
5437             continue;
5438             }
5439         FileRec *child  = iter->second;
5440         ofile->files[fname] = child;
5441       
5442         processDependency(ofile, child);
5443         }
5446     return true;
5453 /**
5454  *  Generate the file dependency list.
5455  */
5456 bool DepTool::generateDependencies()
5458     std::map<String, FileRec *>::iterator iter;
5459     //# First pass.  Scan for all includes
5460     for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5461         {
5462         FileRec *frec = iter->second;
5463         if (!scanFile(iter->first, frec))
5464             {
5465             //quit?
5466             }
5467         }
5469     //# Second pass.  Scan for all includes
5470     for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5471         {
5472         FileRec *include = iter->second;
5473         if (include->type == FileRec::CFILE)
5474             {
5475             String cFileName   = iter->first;
5476             FileRec *ofile     = new FileRec(FileRec::OFILE);
5477             ofile->path        = include->path;
5478             ofile->baseName    = include->baseName;
5479             ofile->suffix      = include->suffix;
5480             String fname       = include->path;
5481             if (fname.size()>0)
5482                 fname.append("/");
5483             fname.append(include->baseName);
5484             fname.append(".o");
5485             oFiles[fname]    = ofile;
5486             //add the .c file first?   no, don't
5487             //ofile->files[cFileName] = include;
5488             
5489             //trace("ofile:%s", fname.c_str());
5491             processDependency(ofile, include);
5492             }
5493         }
5495       
5496     return true;
5501 /**
5502  *  High-level call to generate deps and optionally save them
5503  */
5504 bool DepTool::generateDependencies(const String &fileName)
5506     if (!createFileList())
5507         return false;
5508     if (!generateDependencies())
5509         return false;
5510     if (!saveDepFile(fileName))
5511         return false;
5512     return true;
5516 /**
5517  *   This saves the dependency cache.
5518  */
5519 bool DepTool::saveDepFile(const String &fileName)
5521     time_t tim;
5522     time(&tim);
5524     FILE *f = fopen(fileName.c_str(), "w");
5525     if (!f)
5526         {
5527         trace("cannot open '%s' for writing", fileName.c_str());
5528         }
5529     fprintf(f, "<?xml version='1.0'?>\n");
5530     fprintf(f, "<!--\n");
5531     fprintf(f, "########################################################\n");
5532     fprintf(f, "## File: build.dep\n");
5533     fprintf(f, "## Generated by BuildTool at :%s", ctime(&tim));
5534     fprintf(f, "########################################################\n");
5535     fprintf(f, "-->\n");
5537     fprintf(f, "<dependencies source='%s'>\n\n", sourceDir.c_str());
5538     std::map<String, FileRec *>::iterator iter;
5539     for (iter=oFiles.begin() ; iter!=oFiles.end() ; iter++)
5540         {
5541         FileRec *frec = iter->second;
5542         if (frec->type == FileRec::OFILE)
5543             {
5544             fprintf(f, "<object path='%s' name='%s' suffix='%s'>\n",
5545                  frec->path.c_str(), frec->baseName.c_str(), frec->suffix.c_str());
5546             std::map<String, FileRec *>::iterator citer;
5547             for (citer=frec->files.begin() ; citer!=frec->files.end() ; citer++)
5548                 {
5549                 String cfname = citer->first;
5550                 fprintf(f, "    <dep name='%s'/>\n", cfname.c_str());
5551                 }
5552             fprintf(f, "</object>\n\n");
5553             }
5554         }
5556     fprintf(f, "</dependencies>\n");
5557     fprintf(f, "\n");
5558     fprintf(f, "<!--\n");
5559     fprintf(f, "########################################################\n");
5560     fprintf(f, "## E N D\n");
5561     fprintf(f, "########################################################\n");
5562     fprintf(f, "-->\n");
5564     fclose(f);
5566     return true;
5572 /**
5573  *   This loads the dependency cache.
5574  */
5575 std::vector<DepRec> DepTool::loadDepFile(const String &depFile)
5577     std::vector<DepRec> result;
5578     
5579     Parser parser;
5580     Element *root = parser.parseFile(depFile.c_str());
5581     if (!root)
5582         {
5583         //error("Could not open %s for reading", depFile.c_str());
5584         return result;
5585         }
5587     if (root->getChildren().size()==0 ||
5588         root->getChildren()[0]->getName()!="dependencies")
5589         {
5590         error("loadDepFile: main xml element should be <dependencies>");
5591         delete root;
5592         return result;
5593         }
5595     //########## Start parsing
5596     Element *depList = root->getChildren()[0];
5598     std::vector<Element *> objects = depList->getChildren();
5599     for (unsigned int i=0 ; i<objects.size() ; i++)
5600         {
5601         Element *objectElem = objects[i];
5602         String tagName = objectElem->getName();
5603         if (tagName != "object")
5604             {
5605             error("loadDepFile: <dependencies> should have only <object> children");
5606             return result;
5607             }
5609         String objName   = objectElem->getAttribute("name");
5610          //trace("object:%s", objName.c_str());
5611         DepRec depObject(objName);
5612         depObject.path   = objectElem->getAttribute("path");
5613         depObject.suffix = objectElem->getAttribute("suffix");
5614         //########## DESCRIPTION
5615         std::vector<Element *> depElems = objectElem->getChildren();
5616         for (unsigned int i=0 ; i<depElems.size() ; i++)
5617             {
5618             Element *depElem = depElems[i];
5619             tagName = depElem->getName();
5620             if (tagName != "dep")
5621                 {
5622                 error("loadDepFile: <object> should have only <dep> children");
5623                 return result;
5624                 }
5625             String depName = depElem->getAttribute("name");
5626             //trace("    dep:%s", depName.c_str());
5627             depObject.files.push_back(depName);
5628             }
5630         //Insert into the result list, in a sorted manner
5631         bool inserted = false;
5632         std::vector<DepRec>::iterator iter;
5633         for (iter = result.begin() ; iter != result.end() ; iter++)
5634             {
5635             String vpath = iter->path;
5636             vpath.append("/");
5637             vpath.append(iter->name);
5638             String opath = depObject.path;
5639             opath.append("/");
5640             opath.append(depObject.name);
5641             if (vpath > opath)
5642                 {
5643                 inserted = true;
5644                 iter = result.insert(iter, depObject);
5645                 break;
5646                 }
5647             }
5648         if (!inserted)
5649             result.push_back(depObject);
5650         }
5652     delete root;
5654     return result;
5658 /**
5659  *   This loads the dependency cache.
5660  */
5661 std::vector<DepRec> DepTool::getDepFile(const String &depFile,
5662                    bool forceRefresh)
5664     std::vector<DepRec> result;
5665     if (forceRefresh)
5666         {
5667         generateDependencies(depFile);
5668         result = loadDepFile(depFile);
5669         }
5670     else
5671         {
5672         //try once
5673         result = loadDepFile(depFile);
5674         if (result.size() == 0)
5675             {
5676             //fail? try again
5677             generateDependencies(depFile);
5678             result = loadDepFile(depFile);
5679             }
5680         }
5681     return result;
5687 //########################################################################
5688 //# T A S K
5689 //########################################################################
5690 //forward decl
5691 class Target;
5692 class Make;
5694 /**
5695  *
5696  */
5697 class Task : public MakeBase
5700 public:
5702     typedef enum
5703         {
5704         TASK_NONE,
5705         TASK_CC,
5706         TASK_COPY,
5707         TASK_DELETE,
5708         TASK_JAR,
5709         TASK_JAVAC,
5710         TASK_LINK,
5711         TASK_MAKEFILE,
5712         TASK_MKDIR,
5713         TASK_MSGFMT,
5714         TASK_RANLIB,
5715         TASK_RC,
5716         TASK_SHAREDLIB,
5717         TASK_STATICLIB,
5718         TASK_STRIP,
5719         TASK_TOUCH,
5720         TASK_TSTAMP
5721         } TaskType;
5722         
5724     /**
5725      *
5726      */
5727     Task(MakeBase &par) : parent(par)
5728         { init(); }
5730     /**
5731      *
5732      */
5733     Task(const Task &other) : parent(other.parent)
5734         { init(); assign(other); }
5736     /**
5737      *
5738      */
5739     Task &operator=(const Task &other)
5740         { assign(other); return *this; }
5742     /**
5743      *
5744      */
5745     virtual ~Task()
5746         { }
5749     /**
5750      *
5751      */
5752     virtual MakeBase &getParent()
5753         { return parent; }
5755      /**
5756      *
5757      */
5758     virtual int  getType()
5759         { return type; }
5761     /**
5762      *
5763      */
5764     virtual void setType(int val)
5765         { type = val; }
5767     /**
5768      *
5769      */
5770     virtual String getName()
5771         { return name; }
5773     /**
5774      *
5775      */
5776     virtual bool execute()
5777         { return true; }
5779     /**
5780      *
5781      */
5782     virtual bool parse(Element *elem)
5783         { return true; }
5785     /**
5786      *
5787      */
5788     Task *createTask(Element *elem, int lineNr);
5791 protected:
5793     void init()
5794         {
5795         type = TASK_NONE;
5796         name = "none";
5797         }
5799     void assign(const Task &other)
5800         {
5801         type = other.type;
5802         name = other.name;
5803         }
5804         
5805     String getAttribute(Element *elem, const String &attrName)
5806         {
5807         String str;
5808         return str;
5809         }
5811     MakeBase &parent;
5813     int type;
5815     String name;
5816 };
5820 /**
5821  * This task runs the C/C++ compiler.  The compiler is invoked
5822  * for all .c or .cpp files which are newer than their correcsponding
5823  * .o files.  
5824  */
5825 class TaskCC : public Task
5827 public:
5829     TaskCC(MakeBase &par) : Task(par)
5830         {
5831         type = TASK_CC; name = "cc";
5832         ccCommand   = "gcc";
5833         cxxCommand  = "g++";
5834         source      = ".";
5835         dest        = ".";
5836         flags       = "";
5837         defines     = "";
5838         includes    = "";
5839         fileSet.clear();
5840         }
5842     virtual ~TaskCC()
5843         {}
5845     virtual bool needsCompiling(const FileRec &depRec,
5846               const String &src, const String &dest)
5847         {
5848         return false;
5849         }
5851     virtual bool execute()
5852         {
5853         if (!listFiles(parent, fileSet))
5854             return false;
5855             
5856         FILE *f = NULL;
5857         f = fopen("compile.lst", "w");
5859         bool refreshCache = false;
5860         String fullName = parent.resolve("build.dep");
5861         if (isNewerThan(parent.getURI().getPath(), fullName))
5862             {
5863             status("          : regenerating C/C++ dependency cache");
5864             refreshCache = true;
5865             }
5867         DepTool depTool;
5868         depTool.setSourceDirectory(source);
5869         depTool.setFileList(fileSet.getFiles());
5870         std::vector<DepRec> deps =
5871              depTool.getDepFile("build.dep", refreshCache);
5872         
5873         String incs;
5874         incs.append("-I");
5875         incs.append(parent.resolve("."));
5876         incs.append(" ");
5877         if (includes.size()>0)
5878             {
5879             incs.append(includes);
5880             incs.append(" ");
5881             }
5882         std::set<String> paths;
5883         std::vector<DepRec>::iterator viter;
5884         for (viter=deps.begin() ; viter!=deps.end() ; viter++)
5885             {
5886             DepRec dep = *viter;
5887             if (dep.path.size()>0)
5888                 paths.insert(dep.path);
5889             }
5890         if (source.size()>0)
5891             {
5892             incs.append(" -I");
5893             incs.append(parent.resolve(source));
5894             incs.append(" ");
5895             }
5896         std::set<String>::iterator setIter;
5897         for (setIter=paths.begin() ; setIter!=paths.end() ; setIter++)
5898             {
5899             incs.append(" -I");
5900             String dname;
5901             if (source.size()>0)
5902                 {
5903                 dname.append(source);
5904                 dname.append("/");
5905                 }
5906             dname.append(*setIter);
5907             incs.append(parent.resolve(dname));
5908             }
5909         std::vector<String> cfiles;
5910         for (viter=deps.begin() ; viter!=deps.end() ; viter++)
5911             {
5912             DepRec dep = *viter;
5914             //## Select command
5915             String sfx = dep.suffix;
5916             String command = ccCommand;
5917             if (sfx == "cpp" || sfx == "cxx" || sfx == "c++" ||
5918                  sfx == "cc" || sfx == "CC")
5919                 command = cxxCommand;
5920  
5921             //## Make paths
5922             String destPath = dest;
5923             String srcPath  = source;
5924             if (dep.path.size()>0)
5925                 {
5926                 destPath.append("/");
5927                 destPath.append(dep.path);
5928                 srcPath.append("/");
5929                 srcPath.append(dep.path);
5930                 }
5931             //## Make sure destination directory exists
5932             if (!createDirectory(destPath))
5933                 return false;
5934                 
5935             //## Check whether it needs to be done
5936             String destName;
5937             if (destPath.size()>0)
5938                 {
5939                 destName.append(destPath);
5940                 destName.append("/");
5941                 }
5942             destName.append(dep.name);
5943             destName.append(".o");
5944             String destFullName = parent.resolve(destName);
5945             String srcName;
5946             if (srcPath.size()>0)
5947                 {
5948                 srcName.append(srcPath);
5949                 srcName.append("/");
5950                 }
5951             srcName.append(dep.name);
5952             srcName.append(".");
5953             srcName.append(dep.suffix);
5954             String srcFullName = parent.resolve(srcName);
5955             bool compileMe = false;
5956             //# First we check if the source is newer than the .o
5957             if (isNewerThan(srcFullName, destFullName))
5958                 {
5959                 status("          : compile of %s required by %s",
5960                         destFullName.c_str(), srcFullName.c_str());
5961                 compileMe = true;
5962                 }
5963             else
5964                 {
5965                 //# secondly, we check if any of the included dependencies
5966                 //# of the .c/.cpp is newer than the .o
5967                 for (unsigned int i=0 ; i<dep.files.size() ; i++)
5968                     {
5969                     String depName;
5970                     if (source.size()>0)
5971                         {
5972                         depName.append(source);
5973                         depName.append("/");
5974                         }
5975                     depName.append(dep.files[i]);
5976                     String depFullName = parent.resolve(depName);
5977                     bool depRequires = isNewerThan(depFullName, destFullName);
5978                     //trace("%d %s %s\n", depRequires,
5979                     //        destFullName.c_str(), depFullName.c_str());
5980                     if (depRequires)
5981                         {
5982                         status("          : compile of %s required by %s",
5983                                 destFullName.c_str(), depFullName.c_str());
5984                         compileMe = true;
5985                         break;
5986                         }
5987                     }
5988                 }
5989             if (!compileMe)
5990                 {
5991                 continue;
5992                 }
5994             //## Assemble the command
5995             String cmd = command;
5996             cmd.append(" -c ");
5997             cmd.append(flags);
5998             cmd.append(" ");
5999             cmd.append(defines);
6000             cmd.append(" ");
6001             cmd.append(incs);
6002             cmd.append(" ");
6003             cmd.append(srcFullName);
6004             cmd.append(" -o ");
6005             cmd.append(destFullName);
6007             //## Execute the command
6009             String outString, errString;
6010             bool ret = executeCommand(cmd.c_str(), "", outString, errString);
6012             if (f)
6013                 {
6014                 fprintf(f, "########################### File : %s\n",
6015                              srcFullName.c_str());
6016                 fprintf(f, "#### COMMAND ###\n");
6017                 int col = 0;
6018                 for (int i = 0 ; i < cmd.size() ; i++)
6019                     {
6020                     char ch = cmd[i];
6021                     if (isspace(ch)  && col > 63)
6022                         {
6023                         fputc('\n', f);
6024                         col = 0;
6025                         }
6026                     else
6027                         {
6028                         fputc(ch, f);
6029                         col++;
6030                         }
6031                     if (col > 76)
6032                         {
6033                         fputc('\n', f);
6034                         col = 0;
6035                         }
6036                     }
6037                 fprintf(f, "\n");
6038                 fprintf(f, "#### STDOUT ###\n%s\n", outString.c_str());
6039                 fprintf(f, "#### STDERR ###\n%s\n\n", errString.c_str());
6040                 }
6041             if (!ret)
6042                 {
6043                 error("problem compiling: %s", errString.c_str());
6044                 return false;
6045                 }
6046                 
6047             }
6049         if (f)
6050             {
6051             fclose(f);
6052             }
6053         
6054         return true;
6055         }
6057     virtual bool parse(Element *elem)
6058         {
6059         String s;
6060         if (!parent.getAttribute(elem, "command", s))
6061             return false;
6062         if (s.size()>0) { ccCommand = s; cxxCommand = s; }
6063         if (!parent.getAttribute(elem, "cc", s))
6064             return false;
6065         if (s.size()>0) ccCommand = s;
6066         if (!parent.getAttribute(elem, "cxx", s))
6067             return false;
6068         if (s.size()>0) cxxCommand = s;
6069         if (!parent.getAttribute(elem, "destdir", s))
6070             return false;
6071         if (s.size()>0) dest = s;
6073         std::vector<Element *> children = elem->getChildren();
6074         for (unsigned int i=0 ; i<children.size() ; i++)
6075             {
6076             Element *child = children[i];
6077             String tagName = child->getName();
6078             if (tagName == "flags")
6079                 {
6080                 if (!parent.getValue(child, flags))
6081                     return false;
6082                 flags = strip(flags);
6083                 }
6084             else if (tagName == "includes")
6085                 {
6086                 if (!parent.getValue(child, includes))
6087                     return false;
6088                 includes = strip(includes);
6089                 }
6090             else if (tagName == "defines")
6091                 {
6092                 if (!parent.getValue(child, defines))
6093                     return false;
6094                 defines = strip(defines);
6095                 }
6096             else if (tagName == "fileset")
6097                 {
6098                 if (!parseFileSet(child, parent, fileSet))
6099                     return false;
6100                 source = fileSet.getDirectory();
6101                 }
6102             }
6104         return true;
6105         }
6106         
6107 protected:
6109     String ccCommand;
6110     String cxxCommand;
6111     String source;
6112     String dest;
6113     String flags;
6114     String defines;
6115     String includes;
6116     FileSet fileSet;
6117     
6118 };
6122 /**
6123  *
6124  */
6125 class TaskCopy : public Task
6127 public:
6129     typedef enum
6130         {
6131         CP_NONE,
6132         CP_TOFILE,
6133         CP_TODIR
6134         } CopyType;
6136     TaskCopy(MakeBase &par) : Task(par)
6137         {
6138         type = TASK_COPY; name = "copy";
6139         cptype = CP_NONE;
6140         verbose = false;
6141         haveFileSet = false;
6142         }
6144     virtual ~TaskCopy()
6145         {}
6147     virtual bool execute()
6148         {
6149         switch (cptype)
6150            {
6151            case CP_TOFILE:
6152                {
6153                if (fileName.size()>0)
6154                    {
6155                    status("          : %s to %s",
6156                         fileName.c_str(), toFileName.c_str());
6157                    String fullSource = parent.resolve(fileName);
6158                    String fullDest = parent.resolve(toFileName);
6159                    //trace("copy %s to file %s", fullSource.c_str(),
6160                    //                       fullDest.c_str());
6161                    if (!isRegularFile(fullSource))
6162                        {
6163                        error("copy : file %s does not exist", fullSource.c_str());
6164                        return false;
6165                        }
6166                    if (!isNewerThan(fullSource, fullDest))
6167                        {
6168                        status("          : skipped");
6169                        return true;
6170                        }
6171                    if (!copyFile(fullSource, fullDest))
6172                        return false;
6173                    status("          : 1 file copied");
6174                    }
6175                return true;
6176                }
6177            case CP_TODIR:
6178                {
6179                if (haveFileSet)
6180                    {
6181                    if (!listFiles(parent, fileSet))
6182                        return false;
6183                    String fileSetDir = fileSet.getDirectory();
6185                    status("          : %s to %s",
6186                        fileSetDir.c_str(), toDirName.c_str());
6188                    int nrFiles = 0;
6189                    for (unsigned int i=0 ; i<fileSet.size() ; i++)
6190                        {
6191                        String fileName = fileSet[i];
6193                        String sourcePath;
6194                        if (fileSetDir.size()>0)
6195                            {
6196                            sourcePath.append(fileSetDir);
6197                            sourcePath.append("/");
6198                            }
6199                        sourcePath.append(fileName);
6200                        String fullSource = parent.resolve(sourcePath);
6201                        
6202                        //Get the immediate parent directory's base name
6203                        String baseFileSetDir = fileSetDir;
6204                        unsigned int pos = baseFileSetDir.find_last_of('/');
6205                        if (pos!=baseFileSetDir.npos &&
6206                                   pos < baseFileSetDir.size()-1)
6207                            baseFileSetDir =
6208                               baseFileSetDir.substr(pos+1,
6209                                    baseFileSetDir.size());
6210                        //Now make the new path
6211                        String destPath;
6212                        if (toDirName.size()>0)
6213                            {
6214                            destPath.append(toDirName);
6215                            destPath.append("/");
6216                            }
6217                        if (baseFileSetDir.size()>0)
6218                            {
6219                            destPath.append(baseFileSetDir);
6220                            destPath.append("/");
6221                            }
6222                        destPath.append(fileName);
6223                        String fullDest = parent.resolve(destPath);
6224                        //trace("fileName:%s", fileName.c_str());
6225                        //trace("copy %s to new dir : %s", fullSource.c_str(),
6226                        //                   fullDest.c_str());
6227                        if (!isNewerThan(fullSource, fullDest))
6228                            {
6229                            //trace("copy skipping %s", fullSource.c_str());
6230                            continue;
6231                            }
6232                        if (!copyFile(fullSource, fullDest))
6233                            return false;
6234                        nrFiles++;
6235                        }
6236                    status("          : %d file(s) copied", nrFiles);
6237                    }
6238                else //file source
6239                    {
6240                    //For file->dir we want only the basename of
6241                    //the source appended to the dest dir
6242                    status("          : %s to %s", 
6243                        fileName.c_str(), toDirName.c_str());
6244                    String baseName = fileName;
6245                    unsigned int pos = baseName.find_last_of('/');
6246                    if (pos!=baseName.npos && pos<baseName.size()-1)
6247                        baseName = baseName.substr(pos+1, baseName.size());
6248                    String fullSource = parent.resolve(fileName);
6249                    String destPath;
6250                    if (toDirName.size()>0)
6251                        {
6252                        destPath.append(toDirName);
6253                        destPath.append("/");
6254                        }
6255                    destPath.append(baseName);
6256                    String fullDest = parent.resolve(destPath);
6257                    //trace("copy %s to new dir : %s", fullSource.c_str(),
6258                    //                       fullDest.c_str());
6259                    if (!isRegularFile(fullSource))
6260                        {
6261                        error("copy : file %s does not exist", fullSource.c_str());
6262                        return false;
6263                        }
6264                    if (!isNewerThan(fullSource, fullDest))
6265                        {
6266                        status("          : skipped");
6267                        return true;
6268                        }
6269                    if (!copyFile(fullSource, fullDest))
6270                        return false;
6271                    status("          : 1 file copied");
6272                    }
6273                return true;
6274                }
6275            }
6276         return true;
6277         }
6280     virtual bool parse(Element *elem)
6281         {
6282         if (!parent.getAttribute(elem, "file", fileName))
6283             return false;
6284         if (!parent.getAttribute(elem, "tofile", toFileName))
6285             return false;
6286         if (toFileName.size() > 0)
6287             cptype = CP_TOFILE;
6288         if (!parent.getAttribute(elem, "todir", toDirName))
6289             return false;
6290         if (toDirName.size() > 0)
6291             cptype = CP_TODIR;
6292         String ret;
6293         if (!parent.getAttribute(elem, "verbose", ret))
6294             return false;
6295         if (ret.size()>0 && !getBool(ret, verbose))
6296             return false;
6297             
6298         haveFileSet = false;
6299         
6300         std::vector<Element *> children = elem->getChildren();
6301         for (unsigned int i=0 ; i<children.size() ; i++)
6302             {
6303             Element *child = children[i];
6304             String tagName = child->getName();
6305             if (tagName == "fileset")
6306                 {
6307                 if (!parseFileSet(child, parent, fileSet))
6308                     {
6309                     error("problem getting fileset");
6310                     return false;
6311                     }
6312                 haveFileSet = true;
6313                 }
6314             }
6316         //Perform validity checks
6317         if (fileName.size()>0 && fileSet.size()>0)
6318             {
6319             error("<copy> can only have one of : file= and <fileset>");
6320             return false;
6321             }
6322         if (toFileName.size()>0 && toDirName.size()>0)
6323             {
6324             error("<copy> can only have one of : tofile= or todir=");
6325             return false;
6326             }
6327         if (haveFileSet && toDirName.size()==0)
6328             {
6329             error("a <copy> task with a <fileset> must have : todir=");
6330             return false;
6331             }
6332         if (cptype == CP_TOFILE && fileName.size()==0)
6333             {
6334             error("<copy> tofile= must be associated with : file=");
6335             return false;
6336             }
6337         if (cptype == CP_TODIR && fileName.size()==0 && !haveFileSet)
6338             {
6339             error("<copy> todir= must be associated with : file= or <fileset>");
6340             return false;
6341             }
6343         return true;
6344         }
6345         
6346 private:
6348     int cptype;
6349     String fileName;
6350     FileSet fileSet;
6351     String toFileName;
6352     String toDirName;
6353     bool verbose;
6354     bool haveFileSet;
6355 };
6358 /**
6359  *
6360  */
6361 class TaskDelete : public Task
6363 public:
6365     typedef enum
6366         {
6367         DEL_FILE,
6368         DEL_DIR,
6369         DEL_FILESET
6370         } DeleteType;
6372     TaskDelete(MakeBase &par) : Task(par)
6373         { 
6374           type        = TASK_DELETE;
6375           name        = "delete";
6376           delType     = DEL_FILE;
6377           verbose     = false;
6378           quiet       = false;
6379           failOnError = true;
6380         }
6382     virtual ~TaskDelete()
6383         {}
6385     virtual bool execute()
6386         {
6387         struct stat finfo;
6388         switch (delType)
6389             {
6390             case DEL_FILE:
6391                 {
6392                 status("          : %s", fileName.c_str());
6393                 String fullName = parent.resolve(fileName);
6394                 char *fname = (char *)fullName.c_str();
6395                 //does not exist
6396                 if (stat(fname, &finfo)<0)
6397                     return true;
6398                 //exists but is not a regular file
6399                 if (!S_ISREG(finfo.st_mode))
6400                     {
6401                     error("<delete> failed. '%s' exists and is not a regular file",
6402                           fname);
6403                     return false;
6404                     }
6405                 if (remove(fname)<0)
6406                     {
6407                     error("<delete> failed: %s", strerror(errno));
6408                     return false;
6409                     }
6410                 return true;
6411                 }
6412             case DEL_DIR:
6413                 {
6414                 status("          : %s", dirName.c_str());
6415                 String fullDir = parent.resolve(dirName);
6416                 if (!removeDirectory(fullDir))
6417                     return false;
6418                 return true;
6419                 }
6420             }
6421         return true;
6422         }
6424     virtual bool parse(Element *elem)
6425         {
6426         if (!parent.getAttribute(elem, "file", fileName))
6427             return false;
6428         if (fileName.size() > 0)
6429             delType = DEL_FILE;
6430         if (!parent.getAttribute(elem, "dir", dirName))
6431             return false;
6432         if (dirName.size() > 0)
6433             delType = DEL_DIR;
6434         if (fileName.size()>0 && dirName.size()>0)
6435             {
6436             error("<delete> can have one attribute of file= or dir=");
6437             return false;
6438             }
6439         if (fileName.size()==0 && dirName.size()==0)
6440             {
6441             error("<delete> must have one attribute of file= or dir=");
6442             return false;
6443             }
6444         String ret;
6445         if (!parent.getAttribute(elem, "verbose", ret))
6446             return false;
6447         if (ret.size()>0 && !getBool(ret, verbose))
6448             return false;
6449         if (!parent.getAttribute(elem, "quiet", ret))
6450             return false;
6451         if (ret.size()>0 && !getBool(ret, quiet))
6452             return false;
6453         if (!parent.getAttribute(elem, "failonerror", ret))
6454             return false;
6455         if (ret.size()>0 && !getBool(ret, failOnError))
6456             return false;
6457         return true;
6458         }
6460 private:
6462     int delType;
6463     String dirName;
6464     String fileName;
6465     bool verbose;
6466     bool quiet;
6467     bool failOnError;
6468 };
6471 /**
6472  *
6473  */
6474 class TaskJar : public Task
6476 public:
6478     TaskJar(MakeBase &par) : Task(par)
6479         { type = TASK_JAR; name = "jar"; }
6481     virtual ~TaskJar()
6482         {}
6484     virtual bool execute()
6485         {
6486         return true;
6487         }
6489     virtual bool parse(Element *elem)
6490         {
6491         return true;
6492         }
6493 };
6496 /**
6497  *
6498  */
6499 class TaskJavac : public Task
6501 public:
6503     TaskJavac(MakeBase &par) : Task(par)
6504         { type = TASK_JAVAC; name = "javac"; }
6506     virtual ~TaskJavac()
6507         {}
6509     virtual bool execute()
6510         {
6511         return true;
6512         }
6514     virtual bool parse(Element *elem)
6515         {
6516         return true;
6517         }
6518 };
6521 /**
6522  *
6523  */
6524 class TaskLink : public Task
6526 public:
6528     TaskLink(MakeBase &par) : Task(par)
6529         {
6530         type = TASK_LINK; name = "link";
6531         command = "g++";
6532         doStrip = false;
6533                 stripCommand = "strip";
6534                 objcopyCommand = "objcopy";
6535         }
6537     virtual ~TaskLink()
6538         {}
6540     virtual bool execute()
6541         {
6542         if (!listFiles(parent, fileSet))
6543             return false;
6544         String fileSetDir = fileSet.getDirectory();
6545         //trace("%d files in %s", fileSet.size(), fileSetDir.c_str());
6546         bool doit = false;
6547         String fullTarget = parent.resolve(fileName);
6548         String cmd = command;
6549         cmd.append(" -o ");
6550         cmd.append(fullTarget);
6551         cmd.append(" ");
6552         cmd.append(flags);
6553         for (unsigned int i=0 ; i<fileSet.size() ; i++)
6554             {
6555             cmd.append(" ");
6556             String obj;
6557             if (fileSetDir.size()>0)
6558                 {
6559                 obj.append(fileSetDir);
6560                 obj.append("/");
6561                 }
6562             obj.append(fileSet[i]);
6563             String fullObj = parent.resolve(obj);
6564             cmd.append(fullObj);
6565             //trace("link: tgt:%s obj:%s", fullTarget.c_str(),
6566             //          fullObj.c_str());
6567             if (isNewerThan(fullObj, fullTarget))
6568                 doit = true;
6569             }
6570         cmd.append(" ");
6571         cmd.append(libs);
6572         if (!doit)
6573             {
6574             //trace("link not needed");
6575             return true;
6576             }
6577         //trace("LINK cmd:%s", cmd.c_str());
6580         String outbuf, errbuf;
6581         if (!executeCommand(cmd.c_str(), "", outbuf, errbuf))
6582             {
6583             error("LINK problem: %s", errbuf.c_str());
6584             return false;
6585             }
6587         if (symFileName.size()>0)
6588             {
6589             String symFullName = parent.resolve(symFileName);
6590             cmd = objcopyCommand;
6591             cmd.append(" --only-keep-debug ");
6592             cmd.append(getNativePath(fullTarget));
6593             cmd.append(" ");
6594             cmd.append(getNativePath(symFullName));
6595             if (!executeCommand(cmd, "", outbuf, errbuf))
6596                 {
6597                 error("<strip> symbol file failed : %s", errbuf.c_str());
6598                 return false;
6599                 }
6600             }
6601             
6602         if (doStrip)
6603             {
6604             cmd = stripCommand;
6605             cmd.append(" ");
6606             cmd.append(getNativePath(fullTarget));
6607             if (!executeCommand(cmd, "", outbuf, errbuf))
6608                {
6609                error("<strip> failed : %s", errbuf.c_str());
6610                return false;
6611                }
6612             }
6614         return true;
6615         }
6617     virtual bool parse(Element *elem)
6618         {
6619         String s;
6620         if (!parent.getAttribute(elem, "command", s))
6621             return false;
6622         if (s.size()>0)
6623             command = s;
6624         if (!parent.getAttribute(elem, "objcopycommand", s))
6625             return false;
6626         if (s.size()>0)
6627             objcopyCommand = s;
6628         if (!parent.getAttribute(elem, "stripcommand", s))
6629             return false;
6630         if (s.size()>0)
6631             stripCommand = s;
6632         if (!parent.getAttribute(elem, "out", fileName))
6633             return false;
6634         if (!parent.getAttribute(elem, "strip", s))
6635             return false;
6636         if (s.size()>0 && !getBool(s, doStrip))
6637             return false;
6638         if (!parent.getAttribute(elem, "symfile", symFileName))
6639             return false;
6640             
6641         std::vector<Element *> children = elem->getChildren();
6642         for (unsigned int i=0 ; i<children.size() ; i++)
6643             {
6644             Element *child = children[i];
6645             String tagName = child->getName();
6646             if (tagName == "fileset")
6647                 {
6648                 if (!parseFileSet(child, parent, fileSet))
6649                     return false;
6650                 }
6651             else if (tagName == "flags")
6652                 {
6653                 if (!parent.getValue(child, flags))
6654                     return false;
6655                 flags = strip(flags);
6656                 }
6657             else if (tagName == "libs")
6658                 {
6659                 if (!parent.getValue(child, libs))
6660                     return false;
6661                 libs = strip(libs);
6662                 }
6663             }
6664         return true;
6665         }
6667 private:
6669     String  command;
6670     String  fileName;
6671     String  flags;
6672     String  libs;
6673     FileSet fileSet;
6674     bool    doStrip;
6675     String  symFileName;
6676     String  stripCommand;
6677     String  objcopyCommand;
6679 };
6683 /**
6684  * Create a named directory
6685  */
6686 class TaskMakeFile : public Task
6688 public:
6690     TaskMakeFile(MakeBase &par) : Task(par)
6691         { type = TASK_MAKEFILE; name = "makefile"; }
6693     virtual ~TaskMakeFile()
6694         {}
6696     virtual bool execute()
6697         {
6698         status("          : %s", fileName.c_str());
6699         String fullName = parent.resolve(fileName);
6700         if (!isNewerThan(parent.getURI().getPath(), fullName))
6701             {
6702             //trace("skipped <makefile>");
6703             return true;
6704             }
6705         //trace("fullName:%s", fullName.c_str());
6706         FILE *f = fopen(fullName.c_str(), "w");
6707         if (!f)
6708             {
6709             error("<makefile> could not open %s for writing : %s",
6710                 fullName.c_str(), strerror(errno));
6711             return false;
6712             }
6713         for (unsigned int i=0 ; i<text.size() ; i++)
6714             fputc(text[i], f);
6715         fputc('\n', f);
6716         fclose(f);
6717         return true;
6718         }
6720     virtual bool parse(Element *elem)
6721         {
6722         if (!parent.getAttribute(elem, "file", fileName))
6723             return false;
6724         if (fileName.size() == 0)
6725             {
6726             error("<makefile> requires 'file=\"filename\"' attribute");
6727             return false;
6728             }
6729         if (!parent.getValue(elem, text))
6730             return false;
6731         text = leftJustify(text);
6732         //trace("dirname:%s", dirName.c_str());
6733         return true;
6734         }
6736 private:
6738     String fileName;
6739     String text;
6740 };
6744 /**
6745  * Create a named directory
6746  */
6747 class TaskMkDir : public Task
6749 public:
6751     TaskMkDir(MakeBase &par) : Task(par)
6752         { type = TASK_MKDIR; name = "mkdir"; }
6754     virtual ~TaskMkDir()
6755         {}
6757     virtual bool execute()
6758         {
6759         status("          : %s", dirName.c_str());
6760         String fullDir = parent.resolve(dirName);
6761         //trace("fullDir:%s", fullDir.c_str());
6762         if (!createDirectory(fullDir))
6763             return false;
6764         return true;
6765         }
6767     virtual bool parse(Element *elem)
6768         {
6769         if (!parent.getAttribute(elem, "dir", dirName))
6770             return false;
6771         if (dirName.size() == 0)
6772             {
6773             error("<mkdir> requires 'dir=\"dirname\"' attribute");
6774             return false;
6775             }
6776         return true;
6777         }
6779 private:
6781     String dirName;
6782 };
6786 /**
6787  * Create a named directory
6788  */
6789 class TaskMsgFmt: public Task
6791 public:
6793     TaskMsgFmt(MakeBase &par) : Task(par)
6794          {
6795          type    = TASK_MSGFMT;
6796          name    = "msgfmt";
6797          command = "msgfmt";
6798          owndir  = false;
6799          outName = "";
6800          }
6802     virtual ~TaskMsgFmt()
6803         {}
6805     virtual bool execute()
6806         {
6807         if (!listFiles(parent, fileSet))
6808             return false;
6809         String fileSetDir = fileSet.getDirectory();
6811         //trace("msgfmt: %d", fileSet.size());
6812         for (unsigned int i=0 ; i<fileSet.size() ; i++)
6813             {
6814             String fileName = fileSet[i];
6815             if (getSuffix(fileName) != "po")
6816                 continue;
6817             String sourcePath;
6818             if (fileSetDir.size()>0)
6819                 {
6820                 sourcePath.append(fileSetDir);
6821                 sourcePath.append("/");
6822                 }
6823             sourcePath.append(fileName);
6824             String fullSource = parent.resolve(sourcePath);
6826             String destPath;
6827             if (toDirName.size()>0)
6828                 {
6829                 destPath.append(toDirName);
6830                 destPath.append("/");
6831                 }
6832             if (owndir)
6833                 {
6834                 String subdir = fileName;
6835                 unsigned int pos = subdir.find_last_of('.');
6836                 if (pos != subdir.npos)
6837                     subdir = subdir.substr(0, pos);
6838                 destPath.append(subdir);
6839                 destPath.append("/");
6840                 }
6841             //Pick the output file name
6842             if (outName.size() > 0)
6843                 {
6844                 destPath.append(outName);
6845                 }
6846             else
6847                 {
6848                 destPath.append(fileName);
6849                 destPath[destPath.size()-2] = 'm';
6850                 }
6852             String fullDest = parent.resolve(destPath);
6854             if (!isNewerThan(fullSource, fullDest))
6855                 {
6856                 //trace("skip %s", fullSource.c_str());
6857                 continue;
6858                 }
6859                 
6860             String cmd = command;
6861             cmd.append(" ");
6862             cmd.append(fullSource);
6863             cmd.append(" -o ");
6864             cmd.append(fullDest);
6865             
6866             int pos = fullDest.find_last_of('/');
6867             if (pos>0)
6868                 {
6869                 String fullDestPath = fullDest.substr(0, pos);
6870                 if (!createDirectory(fullDestPath))
6871                     return false;
6872                 }
6876             String outString, errString;
6877             if (!executeCommand(cmd.c_str(), "", outString, errString))
6878                 {
6879                 error("<msgfmt> problem: %s", errString.c_str());
6880                 return false;
6881                 }
6882             }
6884         return true;
6885         }
6887     virtual bool parse(Element *elem)
6888         {
6889         String s;
6890         if (!parent.getAttribute(elem, "command", s))
6891             return false;
6892         if (s.size()>0)
6893             command = s;
6894         if (!parent.getAttribute(elem, "todir", toDirName))
6895             return false;
6896         if (!parent.getAttribute(elem, "out", outName))
6897             return false;
6898         if (!parent.getAttribute(elem, "owndir", s))
6899             return false;
6900         if (s.size()>0 && !getBool(s, owndir))
6901             return false;
6902             
6903         std::vector<Element *> children = elem->getChildren();
6904         for (unsigned int i=0 ; i<children.size() ; i++)
6905             {
6906             Element *child = children[i];
6907             String tagName = child->getName();
6908             if (tagName == "fileset")
6909                 {
6910                 if (!parseFileSet(child, parent, fileSet))
6911                     return false;
6912                 }
6913             }
6914         return true;
6915         }
6917 private:
6919     String  command;
6920     String  toDirName;
6921     String  outName;
6922     FileSet fileSet;
6923     bool    owndir;
6925 };
6931 /**
6932  *  Process an archive to allow random access
6933  */
6934 class TaskRanlib : public Task
6936 public:
6938     TaskRanlib(MakeBase &par) : Task(par)
6939         {
6940         type = TASK_RANLIB; name = "ranlib";
6941         command = "ranlib";
6942         }
6944     virtual ~TaskRanlib()
6945         {}
6947     virtual bool execute()
6948         {
6949         String fullName = parent.resolve(fileName);
6950         //trace("fullDir:%s", fullDir.c_str());
6951         String cmd = command;
6952         cmd.append(" ");
6953         cmd.append(fullName);
6954         String outbuf, errbuf;
6955         if (!executeCommand(cmd, "", outbuf, errbuf))
6956             return false;
6957         return true;
6958         }
6960     virtual bool parse(Element *elem)
6961         {
6962         String s;
6963         if (!parent.getAttribute(elem, "command", s))
6964             return false;
6965         if (s.size()>0)
6966            command = s;
6967         if (!parent.getAttribute(elem, "file", fileName))
6968             return false;
6969         if (fileName.size() == 0)
6970             {
6971             error("<ranlib> requires 'file=\"fileNname\"' attribute");
6972             return false;
6973             }
6974         return true;
6975         }
6977 private:
6979     String fileName;
6980     String command;
6981 };
6985 /**
6986  * Run the "ar" command to archive .o's into a .a
6987  */
6988 class TaskRC : public Task
6990 public:
6992     TaskRC(MakeBase &par) : Task(par)
6993         {
6994         type = TASK_RC; name = "rc";
6995         command = "windres";
6996         }
6998     virtual ~TaskRC()
6999         {}
7001     virtual bool execute()
7002         {
7003         String fullFile = parent.resolve(fileName);
7004         String fullOut  = parent.resolve(outName);
7005         if (!isNewerThan(fullFile, fullOut))
7006             return true;
7007         String cmd = command;
7008         cmd.append(" -o ");
7009         cmd.append(fullOut);
7010         cmd.append(" ");
7011         cmd.append(flags);
7012         cmd.append(" ");
7013         cmd.append(fullFile);
7015         String outString, errString;
7016         if (!executeCommand(cmd.c_str(), "", outString, errString))
7017             {
7018             error("RC problem: %s", errString.c_str());
7019             return false;
7020             }
7021         return true;
7022         }
7024     virtual bool parse(Element *elem)
7025         {
7026         if (!parent.getAttribute(elem, "command", command))
7027             return false;
7028         if (!parent.getAttribute(elem, "file", fileName))
7029             return false;
7030         if (!parent.getAttribute(elem, "out", outName))
7031             return false;
7032         std::vector<Element *> children = elem->getChildren();
7033         for (unsigned int i=0 ; i<children.size() ; i++)
7034             {
7035             Element *child = children[i];
7036             String tagName = child->getName();
7037             if (tagName == "flags")
7038                 {
7039                 if (!parent.getValue(child, flags))
7040                     return false;
7041                 }
7042             }
7043         return true;
7044         }
7046 private:
7048     String command;
7049     String flags;
7050     String fileName;
7051     String outName;
7053 };
7057 /**
7058  *  Collect .o's into a .so or DLL
7059  */
7060 class TaskSharedLib : public Task
7062 public:
7064     TaskSharedLib(MakeBase &par) : Task(par)
7065         {
7066         type = TASK_SHAREDLIB; name = "dll";
7067         command = "dllwrap";
7068         }
7070     virtual ~TaskSharedLib()
7071         {}
7073     virtual bool execute()
7074         {
7075         //trace("###########HERE %d", fileSet.size());
7076         bool doit = false;
7077         
7078         String fullOut = parent.resolve(fileName);
7079         //trace("ar fullout: %s", fullOut.c_str());
7080         
7081         if (!listFiles(parent, fileSet))
7082             return false;
7083         String fileSetDir = fileSet.getDirectory();
7085         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7086             {
7087             String fname;
7088             if (fileSetDir.size()>0)
7089                 {
7090                 fname.append(fileSetDir);
7091                 fname.append("/");
7092                 }
7093             fname.append(fileSet[i]);
7094             String fullName = parent.resolve(fname);
7095             //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
7096             if (isNewerThan(fullName, fullOut))
7097                 doit = true;
7098             }
7099         //trace("Needs it:%d", doit);
7100         if (!doit)
7101             {
7102             return true;
7103             }
7105         String cmd = "dllwrap";
7106         cmd.append(" -o ");
7107         cmd.append(fullOut);
7108         if (defFileName.size()>0)
7109             {
7110             cmd.append(" --def ");
7111             cmd.append(defFileName);
7112             cmd.append(" ");
7113             }
7114         if (impFileName.size()>0)
7115             {
7116             cmd.append(" --implib ");
7117             cmd.append(impFileName);
7118             cmd.append(" ");
7119             }
7120         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7121             {
7122             String fname;
7123             if (fileSetDir.size()>0)
7124                 {
7125                 fname.append(fileSetDir);
7126                 fname.append("/");
7127                 }
7128             fname.append(fileSet[i]);
7129             String fullName = parent.resolve(fname);
7131             cmd.append(" ");
7132             cmd.append(fullName);
7133             }
7134         cmd.append(" ");
7135         cmd.append(libs);
7137         String outString, errString;
7138         if (!executeCommand(cmd.c_str(), "", outString, errString))
7139             {
7140             error("<sharedlib> problem: %s", errString.c_str());
7141             return false;
7142             }
7144         return true;
7145         }
7147     virtual bool parse(Element *elem)
7148         {
7149         if (!parent.getAttribute(elem, "file", fileName))
7150             return false;
7151         if (!parent.getAttribute(elem, "import", impFileName))
7152             return false;
7153         if (!parent.getAttribute(elem, "def", defFileName))
7154             return false;
7155             
7156         std::vector<Element *> children = elem->getChildren();
7157         for (unsigned int i=0 ; i<children.size() ; i++)
7158             {
7159             Element *child = children[i];
7160             String tagName = child->getName();
7161             if (tagName == "fileset")
7162                 {
7163                 if (!parseFileSet(child, parent, fileSet))
7164                     return false;
7165                 }
7166             else if (tagName == "libs")
7167                 {
7168                 if (!parent.getValue(child, libs))
7169                     return false;
7170                 libs = strip(libs);
7171                 }
7172             }
7173         return true;
7174         }
7176 private:
7178     String command;
7179     String fileName;
7180     String defFileName;
7181     String impFileName;
7182     FileSet fileSet;
7183     String libs;
7185 };
7189 /**
7190  * Run the "ar" command to archive .o's into a .a
7191  */
7192 class TaskStaticLib : public Task
7194 public:
7196     TaskStaticLib(MakeBase &par) : Task(par)
7197         {
7198         type = TASK_STATICLIB; name = "staticlib";
7199         command = "ar crv";
7200         }
7202     virtual ~TaskStaticLib()
7203         {}
7205     virtual bool execute()
7206         {
7207         //trace("###########HERE %d", fileSet.size());
7208         bool doit = false;
7209         
7210         String fullOut = parent.resolve(fileName);
7211         //trace("ar fullout: %s", fullOut.c_str());
7212         
7213         if (!listFiles(parent, fileSet))
7214             return false;
7215         String fileSetDir = fileSet.getDirectory();
7217         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7218             {
7219             String fname;
7220             if (fileSetDir.size()>0)
7221                 {
7222                 fname.append(fileSetDir);
7223                 fname.append("/");
7224                 }
7225             fname.append(fileSet[i]);
7226             String fullName = parent.resolve(fname);
7227             //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
7228             if (isNewerThan(fullName, fullOut))
7229                 doit = true;
7230             }
7231         //trace("Needs it:%d", doit);
7232         if (!doit)
7233             {
7234             return true;
7235             }
7237         String cmd = command;
7238         cmd.append(" ");
7239         cmd.append(fullOut);
7240         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7241             {
7242             String fname;
7243             if (fileSetDir.size()>0)
7244                 {
7245                 fname.append(fileSetDir);
7246                 fname.append("/");
7247                 }
7248             fname.append(fileSet[i]);
7249             String fullName = parent.resolve(fname);
7251             cmd.append(" ");
7252             cmd.append(fullName);
7253             }
7255         String outString, errString;
7256         if (!executeCommand(cmd.c_str(), "", outString, errString))
7257             {
7258             error("<staticlib> problem: %s", errString.c_str());
7259             return false;
7260             }
7262         return true;
7263         }
7266     virtual bool parse(Element *elem)
7267         {
7268         String s;
7269         if (!parent.getAttribute(elem, "command", s))
7270             return false;
7271         if (s.size()>0)
7272             command = s;
7273         if (!parent.getAttribute(elem, "file", fileName))
7274             return false;
7275             
7276         std::vector<Element *> children = elem->getChildren();
7277         for (unsigned int i=0 ; i<children.size() ; i++)
7278             {
7279             Element *child = children[i];
7280             String tagName = child->getName();
7281             if (tagName == "fileset")
7282                 {
7283                 if (!parseFileSet(child, parent, fileSet))
7284                     return false;
7285                 }
7286             }
7287         return true;
7288         }
7290 private:
7292     String command;
7293     String fileName;
7294     FileSet fileSet;
7296 };
7301 /**
7302  * Strip an executable
7303  */
7304 class TaskStrip : public Task
7306 public:
7308     TaskStrip(MakeBase &par) : Task(par)
7309         { type = TASK_STRIP; name = "strip"; }
7311     virtual ~TaskStrip()
7312         {}
7314     virtual bool execute()
7315         {
7316         String fullName = parent.resolve(fileName);
7317         //trace("fullDir:%s", fullDir.c_str());
7318         String cmd;
7319         String outbuf, errbuf;
7321         if (symFileName.size()>0)
7322             {
7323             String symFullName = parent.resolve(symFileName);
7324             cmd = "objcopy --only-keep-debug ";
7325             cmd.append(getNativePath(fullName));
7326             cmd.append(" ");
7327             cmd.append(getNativePath(symFullName));
7328             if (!executeCommand(cmd, "", outbuf, errbuf))
7329                 {
7330                 error("<strip> symbol file failed : %s", errbuf.c_str());
7331                 return false;
7332                 }
7333             }
7334             
7335         cmd = "strip ";
7336         cmd.append(getNativePath(fullName));
7337         if (!executeCommand(cmd, "", outbuf, errbuf))
7338             {
7339             error("<strip> failed : %s", errbuf.c_str());
7340             return false;
7341             }
7342         return true;
7343         }
7345     virtual bool parse(Element *elem)
7346         {
7347         if (!parent.getAttribute(elem, "file", fileName))
7348             return false;
7349         if (!parent.getAttribute(elem, "symfile", symFileName))
7350             return false;
7351         if (fileName.size() == 0)
7352             {
7353             error("<strip> requires 'file=\"fileName\"' attribute");
7354             return false;
7355             }
7356         return true;
7357         }
7359 private:
7361     String fileName;
7362     String symFileName;
7363 };
7366 /**
7367  *
7368  */
7369 class TaskTouch : public Task
7371 public:
7373     TaskTouch(MakeBase &par) : Task(par)
7374         { type = TASK_TOUCH; name = "touch"; }
7376     virtual ~TaskTouch()
7377         {}
7379     virtual bool execute()
7380         {
7381         String fullName = parent.resolve(fileName);
7382         String nativeFile = getNativePath(fullName);
7383         if (!isRegularFile(fullName) && !isDirectory(fullName))
7384             {            
7385             // S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH
7386             int ret = creat(nativeFile.c_str(), 0666);
7387             if (ret != 0) 
7388                 {
7389                 error("<touch> could not create '%s' : %s",
7390                     nativeFile.c_str(), strerror(ret));
7391                 return false;
7392                 }
7393             return true;
7394             }
7395         int ret = utime(nativeFile.c_str(), (struct utimbuf *)0);
7396         if (ret != 0)
7397             {
7398             error("<touch> could not update the modification time for '%s' : %s",
7399                 nativeFile.c_str(), strerror(ret));
7400             return false;
7401             }
7402         return true;
7403         }
7405     virtual bool parse(Element *elem)
7406         {
7407         //trace("touch parse");
7408         if (!parent.getAttribute(elem, "file", fileName))
7409             return false;
7410         if (fileName.size() == 0)
7411             {
7412             error("<touch> requires 'file=\"fileName\"' attribute");
7413             return false;
7414             }
7415         return true;
7416         }
7418     String fileName;
7419 };
7422 /**
7423  *
7424  */
7425 class TaskTstamp : public Task
7427 public:
7429     TaskTstamp(MakeBase &par) : Task(par)
7430         { type = TASK_TSTAMP; name = "tstamp"; }
7432     virtual ~TaskTstamp()
7433         {}
7435     virtual bool execute()
7436         {
7437         return true;
7438         }
7440     virtual bool parse(Element *elem)
7441         {
7442         //trace("tstamp parse");
7443         return true;
7444         }
7445 };
7449 /**
7450  *
7451  */
7452 Task *Task::createTask(Element *elem, int lineNr)
7454     String tagName = elem->getName();
7455     //trace("task:%s", tagName.c_str());
7456     Task *task = NULL;
7457     if (tagName == "cc")
7458         task = new TaskCC(parent);
7459     else if (tagName == "copy")
7460         task = new TaskCopy(parent);
7461     else if (tagName == "delete")
7462         task = new TaskDelete(parent);
7463     else if (tagName == "jar")
7464         task = new TaskJar(parent);
7465     else if (tagName == "javac")
7466         task = new TaskJavac(parent);
7467     else if (tagName == "link")
7468         task = new TaskLink(parent);
7469     else if (tagName == "makefile")
7470         task = new TaskMakeFile(parent);
7471     else if (tagName == "mkdir")
7472         task = new TaskMkDir(parent);
7473     else if (tagName == "msgfmt")
7474         task = new TaskMsgFmt(parent);
7475     else if (tagName == "ranlib")
7476         task = new TaskRanlib(parent);
7477     else if (tagName == "rc")
7478         task = new TaskRC(parent);
7479     else if (tagName == "sharedlib")
7480         task = new TaskSharedLib(parent);
7481     else if (tagName == "staticlib")
7482         task = new TaskStaticLib(parent);
7483     else if (tagName == "strip")
7484         task = new TaskStrip(parent);
7485     else if (tagName == "touch")
7486         task = new TaskTouch(parent);
7487     else if (tagName == "tstamp")
7488         task = new TaskTstamp(parent);
7489     else
7490         {
7491         error("Unknown task '%s'", tagName.c_str());
7492         return NULL;
7493         }
7495     task->setLine(lineNr);
7497     if (!task->parse(elem))
7498         {
7499         delete task;
7500         return NULL;
7501         }
7502     return task;
7507 //########################################################################
7508 //# T A R G E T
7509 //########################################################################
7511 /**
7512  *
7513  */
7514 class Target : public MakeBase
7517 public:
7519     /**
7520      *
7521      */
7522     Target(Make &par) : parent(par)
7523         { init(); }
7525     /**
7526      *
7527      */
7528     Target(const Target &other) : parent(other.parent)
7529         { init(); assign(other); }
7531     /**
7532      *
7533      */
7534     Target &operator=(const Target &other)
7535         { init(); assign(other); return *this; }
7537     /**
7538      *
7539      */
7540     virtual ~Target()
7541         { cleanup() ; }
7544     /**
7545      *
7546      */
7547     virtual Make &getParent()
7548         { return parent; }
7550     /**
7551      *
7552      */
7553     virtual String getName()
7554         { return name; }
7556     /**
7557      *
7558      */
7559     virtual void setName(const String &val)
7560         { name = val; }
7562     /**
7563      *
7564      */
7565     virtual String getDescription()
7566         { return description; }
7568     /**
7569      *
7570      */
7571     virtual void setDescription(const String &val)
7572         { description = val; }
7574     /**
7575      *
7576      */
7577     virtual void addDependency(const String &val)
7578         { deps.push_back(val); }
7580     /**
7581      *
7582      */
7583     virtual void parseDependencies(const String &val)
7584         { deps = tokenize(val, ", "); }
7586     /**
7587      *
7588      */
7589     virtual std::vector<String> &getDependencies()
7590         { return deps; }
7592     /**
7593      *
7594      */
7595     virtual String getIf()
7596         { return ifVar; }
7598     /**
7599      *
7600      */
7601     virtual void setIf(const String &val)
7602         { ifVar = val; }
7604     /**
7605      *
7606      */
7607     virtual String getUnless()
7608         { return unlessVar; }
7610     /**
7611      *
7612      */
7613     virtual void setUnless(const String &val)
7614         { unlessVar = val; }
7616     /**
7617      *
7618      */
7619     virtual void addTask(Task *val)
7620         { tasks.push_back(val); }
7622     /**
7623      *
7624      */
7625     virtual std::vector<Task *> &getTasks()
7626         { return tasks; }
7628 private:
7630     void init()
7631         {
7632         }
7634     void cleanup()
7635         {
7636         tasks.clear();
7637         }
7639     void assign(const Target &other)
7640         {
7641         //parent      = other.parent;
7642         name        = other.name;
7643         description = other.description;
7644         ifVar       = other.ifVar;
7645         unlessVar   = other.unlessVar;
7646         deps        = other.deps;
7647         tasks       = other.tasks;
7648         }
7650     Make &parent;
7652     String name;
7654     String description;
7656     String ifVar;
7658     String unlessVar;
7660     std::vector<String> deps;
7662     std::vector<Task *> tasks;
7664 };
7673 //########################################################################
7674 //# M A K E
7675 //########################################################################
7678 /**
7679  *
7680  */
7681 class Make : public MakeBase
7684 public:
7686     /**
7687      *
7688      */
7689     Make()
7690         { init(); }
7692     /**
7693      *
7694      */
7695     Make(const Make &other)
7696         { assign(other); }
7698     /**
7699      *
7700      */
7701     Make &operator=(const Make &other)
7702         { assign(other); return *this; }
7704     /**
7705      *
7706      */
7707     virtual ~Make()
7708         { cleanup(); }
7710     /**
7711      *
7712      */
7713     virtual std::map<String, Target> &getTargets()
7714         { return targets; }
7717     /**
7718      *
7719      */
7720     virtual String version()
7721         { return BUILDTOOL_VERSION; }
7723     /**
7724      * Overload a <property>
7725      */
7726     virtual bool specifyProperty(const String &name,
7727                                  const String &value);
7729     /**
7730      *
7731      */
7732     virtual bool run();
7734     /**
7735      *
7736      */
7737     virtual bool run(const String &target);
7741 private:
7743     /**
7744      *
7745      */
7746     void init();
7748     /**
7749      *
7750      */
7751     void cleanup();
7753     /**
7754      *
7755      */
7756     void assign(const Make &other);
7758     /**
7759      *
7760      */
7761     bool executeTask(Task &task);
7764     /**
7765      *
7766      */
7767     bool executeTarget(Target &target,
7768              std::set<String> &targetsCompleted);
7771     /**
7772      *
7773      */
7774     bool execute();
7776     /**
7777      *
7778      */
7779     bool checkTargetDependencies(Target &prop,
7780                     std::vector<String> &depList);
7782     /**
7783      *
7784      */
7785     bool parsePropertyFile(const String &fileName,
7786                            const String &prefix);
7788     /**
7789      *
7790      */
7791     bool parseProperty(Element *elem);
7793     /**
7794      *
7795      */
7796     bool parseFile();
7798     /**
7799      *
7800      */
7801     std::vector<String> glob(const String &pattern);
7804     //###############
7805     //# Fields
7806     //###############
7808     String projectName;
7810     String currentTarget;
7812     String defaultTarget;
7814     String specifiedTarget;
7816     String baseDir;
7818     String description;
7819     
7820     String envAlias;
7822     //std::vector<Property> properties;
7823     
7824     std::map<String, Target> targets;
7826     std::vector<Task *> allTasks;
7827     
7828     std::map<String, String> specifiedProperties;
7830 };
7833 //########################################################################
7834 //# C L A S S  M A I N T E N A N C E
7835 //########################################################################
7837 /**
7838  *
7839  */
7840 void Make::init()
7842     uri             = "build.xml";
7843     projectName     = "";
7844     currentTarget   = "";
7845     defaultTarget   = "";
7846     specifiedTarget = "";
7847     baseDir         = "";
7848     description     = "";
7849     envAlias        = "";
7850     properties.clear();
7851     for (unsigned int i = 0 ; i < allTasks.size() ; i++)
7852         delete allTasks[i];
7853     allTasks.clear();
7858 /**
7859  *
7860  */
7861 void Make::cleanup()
7863     for (unsigned int i = 0 ; i < allTasks.size() ; i++)
7864         delete allTasks[i];
7865     allTasks.clear();
7870 /**
7871  *
7872  */
7873 void Make::assign(const Make &other)
7875     uri              = other.uri;
7876     projectName      = other.projectName;
7877     currentTarget    = other.currentTarget;
7878     defaultTarget    = other.defaultTarget;
7879     specifiedTarget  = other.specifiedTarget;
7880     baseDir          = other.baseDir;
7881     description      = other.description;
7882     properties       = other.properties;
7887 //########################################################################
7888 //# U T I L I T Y    T A S K S
7889 //########################################################################
7891 /**
7892  *  Perform a file globbing
7893  */
7894 std::vector<String> Make::glob(const String &pattern)
7896     std::vector<String> res;
7897     return res;
7901 //########################################################################
7902 //# P U B L I C    A P I
7903 //########################################################################
7907 /**
7908  *
7909  */
7910 bool Make::executeTarget(Target &target,
7911              std::set<String> &targetsCompleted)
7914     String name = target.getName();
7916     //First get any dependencies for this target
7917     std::vector<String> deps = target.getDependencies();
7918     for (unsigned int i=0 ; i<deps.size() ; i++)
7919         {
7920         String dep = deps[i];
7921         //Did we do it already?  Skip
7922         if (targetsCompleted.find(dep)!=targetsCompleted.end())
7923             continue;
7924             
7925         std::map<String, Target> &tgts =
7926                target.getParent().getTargets();
7927         std::map<String, Target>::iterator iter =
7928                tgts.find(dep);
7929         if (iter == tgts.end())
7930             {
7931             error("Target '%s' dependency '%s' not found",
7932                       name.c_str(),  dep.c_str());
7933             return false;
7934             }
7935         Target depTarget = iter->second;
7936         if (!executeTarget(depTarget, targetsCompleted))
7937             {
7938             return false;
7939             }
7940         }
7942     status("## Target : %s", name.c_str());
7944     //Now let's do the tasks
7945     std::vector<Task *> &tasks = target.getTasks();
7946     for (unsigned int i=0 ; i<tasks.size() ; i++)
7947         {
7948         Task *task = tasks[i];
7949         status("---- task : %s", task->getName().c_str());
7950         if (!task->execute())
7951             {
7952             return false;
7953             }
7954         }
7955         
7956     targetsCompleted.insert(name);
7957     
7958     return true;
7963 /**
7964  *  Main execute() method.  Start here and work
7965  *  up the dependency tree 
7966  */
7967 bool Make::execute()
7969     status("######## EXECUTE");
7971     //Determine initial target
7972     if (specifiedTarget.size()>0)
7973         {
7974         currentTarget = specifiedTarget;
7975         }
7976     else if (defaultTarget.size()>0)
7977         {
7978         currentTarget = defaultTarget;
7979         }
7980     else
7981         {
7982         error("execute: no specified or default target requested");
7983         return false;
7984         }
7986     std::map<String, Target>::iterator iter =
7987                targets.find(currentTarget);
7988     if (iter == targets.end())
7989         {
7990         error("Initial target '%s' not found",
7991                  currentTarget.c_str());
7992         return false;
7993         }
7994         
7995     //Now run
7996     Target target = iter->second;
7997     std::set<String> targetsCompleted;
7998     if (!executeTarget(target, targetsCompleted))
7999         {
8000         return false;
8001         }
8003     status("######## EXECUTE COMPLETE");
8004     return true;
8010 /**
8011  *
8012  */
8013 bool Make::checkTargetDependencies(Target &target, 
8014                             std::vector<String> &depList)
8016     String tgtName = target.getName().c_str();
8017     depList.push_back(tgtName);
8019     std::vector<String> deps = target.getDependencies();
8020     for (unsigned int i=0 ; i<deps.size() ; i++)
8021         {
8022         String dep = deps[i];
8023         //First thing entered was the starting Target
8024         if (dep == depList[0])
8025             {
8026             error("Circular dependency '%s' found at '%s'",
8027                       dep.c_str(), tgtName.c_str());
8028             std::vector<String>::iterator diter;
8029             for (diter=depList.begin() ; diter!=depList.end() ; diter++)
8030                 {
8031                 error("  %s", diter->c_str());
8032                 }
8033             return false;
8034             }
8036         std::map<String, Target> &tgts =
8037                   target.getParent().getTargets();
8038         std::map<String, Target>::iterator titer = tgts.find(dep);
8039         if (titer == tgts.end())
8040             {
8041             error("Target '%s' dependency '%s' not found",
8042                       tgtName.c_str(), dep.c_str());
8043             return false;
8044             }
8045         if (!checkTargetDependencies(titer->second, depList))
8046             {
8047             return false;
8048             }
8049         }
8050     return true;
8057 static int getword(int pos, const String &inbuf, String &result)
8059     int p = pos;
8060     int len = (int)inbuf.size();
8061     String val;
8062     while (p < len)
8063         {
8064         char ch = inbuf[p];
8065         if (!isalnum(ch) && ch!='.' && ch!='_')
8066             break;
8067         val.push_back(ch);
8068         p++;
8069         }
8070     result = val;
8071     return p;
8077 /**
8078  *
8079  */
8080 bool Make::parsePropertyFile(const String &fileName,
8081                              const String &prefix)
8083     FILE *f = fopen(fileName.c_str(), "r");
8084     if (!f)
8085         {
8086         error("could not open property file %s", fileName.c_str());
8087         return false;
8088         }
8089     int linenr = 0;
8090     while (!feof(f))
8091         {
8092         char buf[256];
8093         if (!fgets(buf, 255, f))
8094             break;
8095         linenr++;
8096         String s = buf;
8097         s = trim(s);
8098         int len = s.size();
8099         if (len == 0)
8100             continue;
8101         if (s[0] == '#')
8102             continue;
8103         String key;
8104         String val;
8105         int p = 0;
8106         int p2 = getword(p, s, key);
8107         if (p2 <= p)
8108             {
8109             error("property file %s, line %d: expected keyword",
8110                     fileName.c_str(), linenr);
8111             return false;
8112             }
8113         if (prefix.size() > 0)
8114             {
8115             key.insert(0, prefix);
8116             }
8118         //skip whitespace
8119         for (p=p2 ; p<len ; p++)
8120             if (!isspace(s[p]))
8121                 break;
8123         if (p>=len || s[p]!='=')
8124             {
8125             error("property file %s, line %d: expected '='",
8126                     fileName.c_str(), linenr);
8127             return false;
8128             }
8129         p++;
8131         //skip whitespace
8132         for ( ; p<len ; p++)
8133             if (!isspace(s[p]))
8134                 break;
8136         /* This way expects a word after the =
8137         p2 = getword(p, s, val);
8138         if (p2 <= p)
8139             {
8140             error("property file %s, line %d: expected value",
8141                     fileName.c_str(), linenr);
8142             return false;
8143             }
8144         */
8145         // This way gets the rest of the line after the =
8146         if (p>=len)
8147             {
8148             error("property file %s, line %d: expected value",
8149                     fileName.c_str(), linenr);
8150             return false;
8151             }
8152         val = s.substr(p);
8153         if (key.size()==0)
8154             continue;
8155         //allow property to be set, even if val=""
8157         //trace("key:'%s' val:'%s'", key.c_str(), val.c_str());
8158         //See if we wanted to overload this property
8159         std::map<String, String>::iterator iter =
8160             specifiedProperties.find(key);
8161         if (iter!=specifiedProperties.end())
8162             {
8163             val = iter->second;
8164             status("overloading property '%s' = '%s'",
8165                    key.c_str(), val.c_str());
8166             }
8167         properties[key] = val;
8168         }
8169     fclose(f);
8170     return true;
8176 /**
8177  *
8178  */
8179 bool Make::parseProperty(Element *elem)
8181     std::vector<Attribute> &attrs = elem->getAttributes();
8182     for (unsigned int i=0 ; i<attrs.size() ; i++)
8183         {
8184         String attrName = attrs[i].getName();
8185         String attrVal  = attrs[i].getValue();
8187         if (attrName == "name")
8188             {
8189             String val;
8190             if (!getAttribute(elem, "value", val))
8191                 return false;
8192             if (val.size() > 0)
8193                 {
8194                 properties[attrVal] = val;
8195                 }
8196             else
8197                 {
8198                 if (!getAttribute(elem, "location", val))
8199                     return false;
8200                 //let the property exist, even if not defined
8201                 properties[attrVal] = val;
8202                 }
8203             //See if we wanted to overload this property
8204             std::map<String, String>::iterator iter =
8205                 specifiedProperties.find(attrVal);
8206             if (iter != specifiedProperties.end())
8207                 {
8208                 val = iter->second;
8209                 status("overloading property '%s' = '%s'",
8210                     attrVal.c_str(), val.c_str());
8211                 properties[attrVal] = val;
8212                 }
8213             }
8214         else if (attrName == "file")
8215             {
8216             String prefix;
8217             if (!getAttribute(elem, "prefix", prefix))
8218                 return false;
8219             if (prefix.size() > 0)
8220                 {
8221                 if (prefix[prefix.size()-1] != '.')
8222                     prefix.push_back('.');
8223                 }
8224             if (!parsePropertyFile(attrName, prefix))
8225                 return false;
8226             }
8227         else if (attrName == "environment")
8228             {
8229             if (envAlias.size() > 0)
8230                 {
8231                 error("environment property can only be set once");
8232                 return false;
8233                 }
8234             envAlias = attrVal;
8235             }
8236         }
8238     return true;
8244 /**
8245  *
8246  */
8247 bool Make::parseFile()
8249     status("######## PARSE : %s", uri.getPath().c_str());
8251     setLine(0);
8253     Parser parser;
8254     Element *root = parser.parseFile(uri.getNativePath());
8255     if (!root)
8256         {
8257         error("Could not open %s for reading",
8258               uri.getNativePath().c_str());
8259         return false;
8260         }
8261     
8262     setLine(root->getLine());
8264     if (root->getChildren().size()==0 ||
8265         root->getChildren()[0]->getName()!="project")
8266         {
8267         error("Main xml element should be <project>");
8268         delete root;
8269         return false;
8270         }
8272     //########## Project attributes
8273     Element *project = root->getChildren()[0];
8274     String s = project->getAttribute("name");
8275     if (s.size() > 0)
8276         projectName = s;
8277     s = project->getAttribute("default");
8278     if (s.size() > 0)
8279         defaultTarget = s;
8280     s = project->getAttribute("basedir");
8281     if (s.size() > 0)
8282         baseDir = s;
8284     //######### PARSE MEMBERS
8285     std::vector<Element *> children = project->getChildren();
8286     for (unsigned int i=0 ; i<children.size() ; i++)
8287         {
8288         Element *elem = children[i];
8289         setLine(elem->getLine());
8290         String tagName = elem->getName();
8292         //########## DESCRIPTION
8293         if (tagName == "description")
8294             {
8295             description = parser.trim(elem->getValue());
8296             }
8298         //######### PROPERTY
8299         else if (tagName == "property")
8300             {
8301             if (!parseProperty(elem))
8302                 return false;
8303             }
8305         //######### TARGET
8306         else if (tagName == "target")
8307             {
8308             String tname   = elem->getAttribute("name");
8309             String tdesc   = elem->getAttribute("description");
8310             String tdeps   = elem->getAttribute("depends");
8311             String tif     = elem->getAttribute("if");
8312             String tunless = elem->getAttribute("unless");
8313             Target target(*this);
8314             target.setName(tname);
8315             target.setDescription(tdesc);
8316             target.parseDependencies(tdeps);
8317             target.setIf(tif);
8318             target.setUnless(tunless);
8319             std::vector<Element *> telems = elem->getChildren();
8320             for (unsigned int i=0 ; i<telems.size() ; i++)
8321                 {
8322                 Element *telem = telems[i];
8323                 Task breeder(*this);
8324                 Task *task = breeder.createTask(telem, telem->getLine());
8325                 if (!task)
8326                     return false;
8327                 allTasks.push_back(task);
8328                 target.addTask(task);
8329                 }
8331             //Check name
8332             if (tname.size() == 0)
8333                 {
8334                 error("no name for target");
8335                 return false;
8336                 }
8337             //Check for duplicate name
8338             if (targets.find(tname) != targets.end())
8339                 {
8340                 error("target '%s' already defined", tname.c_str());
8341                 return false;
8342                 }
8343             //more work than targets[tname]=target, but avoids default allocator
8344             targets.insert(std::make_pair<String, Target>(tname, target));
8345             }
8346         //######### none of the above
8347         else
8348             {
8349             error("unknown toplevel tag: <%s>", tagName.c_str());
8350             return false;
8351             }
8353         }
8355     std::map<String, Target>::iterator iter;
8356     for (iter = targets.begin() ; iter!= targets.end() ; iter++)
8357         {
8358         Target tgt = iter->second;
8359         std::vector<String> depList;
8360         if (!checkTargetDependencies(tgt, depList))
8361             {
8362             return false;
8363             }
8364         }
8367     delete root;
8368     status("######## PARSE COMPLETE");
8369     return true;
8373 /**
8374  * Overload a <property>
8375  */
8376 bool Make::specifyProperty(const String &name, const String &value)
8378     if (specifiedProperties.find(name) != specifiedProperties.end())
8379         {
8380         error("Property %s already specified", name.c_str());
8381         return false;
8382         }
8383     specifiedProperties[name] = value;
8384     return true;
8389 /**
8390  *
8391  */
8392 bool Make::run()
8394     if (!parseFile())
8395         return false;
8396         
8397     if (!execute())
8398         return false;
8400     return true;
8406 /**
8407  * Get a formatted MM:SS.sss time elapsed string
8408  */ 
8409 static String
8410 timeDiffString(struct timeval &x, struct timeval &y)
8412     long microsX  = x.tv_usec;
8413     long secondsX = x.tv_sec;
8414     long microsY  = y.tv_usec;
8415     long secondsY = y.tv_sec;
8416     if (microsX < microsY)
8417         {
8418         microsX += 1000000;
8419         secondsX -= 1;
8420         }
8422     int seconds = (int)(secondsX - secondsY);
8423     int millis  = (int)((microsX - microsY)/1000);
8425     int minutes = seconds/60;
8426     seconds -= minutes*60;
8427     char buf[80];
8428     snprintf(buf, 79, "%dm %d.%03ds", minutes, seconds, millis);
8429     String ret = buf;
8430     return ret;
8431     
8434 /**
8435  *
8436  */
8437 bool Make::run(const String &target)
8439     status("####################################################");
8440     status("#   %s", version().c_str());
8441     status("####################################################");
8442     struct timeval timeStart, timeEnd;
8443     ::gettimeofday(&timeStart, NULL);
8444     specifiedTarget = target;
8445     if (!run())
8446         return false;
8447     ::gettimeofday(&timeEnd, NULL);
8448     String timeStr = timeDiffString(timeEnd, timeStart);
8449     status("####################################################");
8450     status("#   BuildTool Completed : %s", timeStr.c_str());
8451     status("####################################################");
8452     return true;
8461 }// namespace buildtool
8462 //########################################################################
8463 //# M A I N
8464 //########################################################################
8466 typedef buildtool::String String;
8468 /**
8469  *  Format an error message in printf() style
8470  */
8471 static void error(char *fmt, ...)
8473     va_list ap;
8474     va_start(ap, fmt);
8475     fprintf(stderr, "BuildTool error: ");
8476     vfprintf(stderr, fmt, ap);
8477     fprintf(stderr, "\n");
8478     va_end(ap);
8482 static bool parseProperty(const String &s, String &name, String &val)
8484     int len = s.size();
8485     int i;
8486     for (i=0 ; i<len ; i++)
8487         {
8488         char ch = s[i];
8489         if (ch == '=')
8490             break;
8491         name.push_back(ch);
8492         }
8493     if (i>=len || s[i]!='=')
8494         {
8495         error("property requires -Dname=value");
8496         return false;
8497         }
8498     i++;
8499     for ( ; i<len ; i++)
8500         {
8501         char ch = s[i];
8502         val.push_back(ch);
8503         }
8504     return true;
8508 /**
8509  * Compare a buffer with a key, for the length of the key
8510  */
8511 static bool sequ(const String &buf, char *key)
8513     int len = buf.size();
8514     for (int i=0 ; key[i] && i<len ; i++)
8515         {
8516         if (key[i] != buf[i])
8517             return false;
8518         }        
8519     return true;
8522 static void usage(int argc, char **argv)
8524     printf("usage:\n");
8525     printf("   %s [options] [target]\n", argv[0]);
8526     printf("Options:\n");
8527     printf("  -help, -h              print this message\n");
8528     printf("  -version               print the version information and exit\n");
8529     printf("  -file <file>           use given buildfile\n");
8530     printf("  -f <file>                 ''\n");
8531     printf("  -D<property>=<value>   use value for given property\n");
8537 /**
8538  * Parse the command-line args, get our options,
8539  * and run this thing
8540  */   
8541 static bool parseOptions(int argc, char **argv)
8543     if (argc < 1)
8544         {
8545         error("Cannot parse arguments");
8546         return false;
8547         }
8549     buildtool::Make make;
8551     String target;
8553     //char *progName = argv[0];
8554     for (int i=1 ; i<argc ; i++)
8555         {
8556         String arg = argv[i];
8557         if (arg.size()>1 && arg[0]=='-')
8558             {
8559             if (arg == "-h" || arg == "-help")
8560                 {
8561                 usage(argc,argv);
8562                 return true;
8563                 }
8564             else if (arg == "-version")
8565                 {
8566                 printf("%s", make.version().c_str());
8567                 return true;
8568                 }
8569             else if (arg == "-f" || arg == "-file")
8570                 {
8571                 if (i>=argc)
8572                    {
8573                    usage(argc, argv);
8574                    return false;
8575                    }
8576                 i++; //eat option
8577                 make.setURI(argv[i]);
8578                 }
8579             else if (arg.size()>2 && sequ(arg, "-D"))
8580                 {
8581                 String s = arg.substr(2, s.size());
8582                 String name, value;
8583                 if (!parseProperty(s, name, value))
8584                    {
8585                    usage(argc, argv);
8586                    return false;
8587                    }
8588                 if (!make.specifyProperty(name, value))
8589                     return false;
8590                 }
8591             else
8592                 {
8593                 error("Unknown option:%s", arg.c_str());
8594                 return false;
8595                 }
8596             }
8597         else
8598             {
8599             if (target.size()>0)
8600                 {
8601                 error("only one initial target");
8602                 usage(argc, argv);
8603                 return false;
8604                 }
8605             target = arg;
8606             }
8607         }
8609     //We have the options.  Now execute them
8610     if (!make.run(target))
8611         return false;
8613     return true;
8619 /*
8620 static bool runMake()
8622     buildtool::Make make;
8623     if (!make.run())
8624         return false;
8625     return true;
8629 static bool pkgConfigTest()
8631     buildtool::PkgConfig pkgConfig;
8632     if (!pkgConfig.readFile("gtk+-2.0.pc"))
8633         return false;
8634     return true;
8639 static bool depTest()
8641     buildtool::DepTool deptool;
8642     deptool.setSourceDirectory("/dev/ink/inkscape/src");
8643     if (!deptool.generateDependencies("build.dep"))
8644         return false;
8645     std::vector<buildtool::FileRec> res =
8646            deptool.loadDepFile("build.dep");
8647     if (res.size() == 0)
8648         return false;
8649     return true;
8652 static bool popenTest()
8654     buildtool::Make make;
8655     buildtool::String out, err;
8656     bool ret = make.executeCommand("gcc xx.cpp", "", out, err);
8657     printf("Popen test:%d '%s' '%s'\n", ret, out.c_str(), err.c_str());
8658     return true;
8662 static bool propFileTest()
8664     buildtool::Make make;
8665     make.parsePropertyFile("test.prop", "test.");
8666     return true;
8668 */
8670 int main(int argc, char **argv)
8673     if (!parseOptions(argc, argv))
8674         return 1;
8675     /*
8676     if (!popenTest())
8677         return 1;
8679     if (!depTest())
8680         return 1;
8681     if (!propFileTest())
8682         return 1;
8683     if (runMake())
8684         return 1;
8685     */
8686     return 0;
8690 //########################################################################
8691 //# E N D 
8692 //########################################################################