Code

Fix native path on <fileset>-included objects
[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.13, 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             String nativeFullObj = getNativePath(fullObj);
6565             cmd.append(nativeFullObj);
6566             //trace("link: tgt:%s obj:%s", fullTarget.c_str(),
6567             //          fullObj.c_str());
6568             if (isNewerThan(fullObj, fullTarget))
6569                 doit = true;
6570             }
6571         cmd.append(" ");
6572         cmd.append(libs);
6573         if (!doit)
6574             {
6575             //trace("link not needed");
6576             return true;
6577             }
6578         //trace("LINK cmd:%s", cmd.c_str());
6581         String outbuf, errbuf;
6582         if (!executeCommand(cmd.c_str(), "", outbuf, errbuf))
6583             {
6584             error("LINK problem: %s", errbuf.c_str());
6585             return false;
6586             }
6588         if (symFileName.size()>0)
6589             {
6590             String symFullName = parent.resolve(symFileName);
6591             cmd = objcopyCommand;
6592             cmd.append(" --only-keep-debug ");
6593             cmd.append(getNativePath(fullTarget));
6594             cmd.append(" ");
6595             cmd.append(getNativePath(symFullName));
6596             if (!executeCommand(cmd, "", outbuf, errbuf))
6597                 {
6598                 error("<strip> symbol file failed : %s", errbuf.c_str());
6599                 return false;
6600                 }
6601             }
6602             
6603         if (doStrip)
6604             {
6605             cmd = stripCommand;
6606             cmd.append(" ");
6607             cmd.append(getNativePath(fullTarget));
6608             if (!executeCommand(cmd, "", outbuf, errbuf))
6609                {
6610                error("<strip> failed : %s", errbuf.c_str());
6611                return false;
6612                }
6613             }
6615         return true;
6616         }
6618     virtual bool parse(Element *elem)
6619         {
6620         String s;
6621         if (!parent.getAttribute(elem, "command", s))
6622             return false;
6623         if (s.size()>0)
6624             command = s;
6625         if (!parent.getAttribute(elem, "objcopycommand", s))
6626             return false;
6627         if (s.size()>0)
6628             objcopyCommand = s;
6629         if (!parent.getAttribute(elem, "stripcommand", s))
6630             return false;
6631         if (s.size()>0)
6632             stripCommand = s;
6633         if (!parent.getAttribute(elem, "out", fileName))
6634             return false;
6635         if (!parent.getAttribute(elem, "strip", s))
6636             return false;
6637         if (s.size()>0 && !getBool(s, doStrip))
6638             return false;
6639         if (!parent.getAttribute(elem, "symfile", symFileName))
6640             return false;
6641             
6642         std::vector<Element *> children = elem->getChildren();
6643         for (unsigned int i=0 ; i<children.size() ; i++)
6644             {
6645             Element *child = children[i];
6646             String tagName = child->getName();
6647             if (tagName == "fileset")
6648                 {
6649                 if (!parseFileSet(child, parent, fileSet))
6650                     return false;
6651                 }
6652             else if (tagName == "flags")
6653                 {
6654                 if (!parent.getValue(child, flags))
6655                     return false;
6656                 flags = strip(flags);
6657                 }
6658             else if (tagName == "libs")
6659                 {
6660                 if (!parent.getValue(child, libs))
6661                     return false;
6662                 libs = strip(libs);
6663                 }
6664             }
6665         return true;
6666         }
6668 private:
6670     String  command;
6671     String  fileName;
6672     String  flags;
6673     String  libs;
6674     FileSet fileSet;
6675     bool    doStrip;
6676     String  symFileName;
6677     String  stripCommand;
6678     String  objcopyCommand;
6680 };
6684 /**
6685  * Create a named directory
6686  */
6687 class TaskMakeFile : public Task
6689 public:
6691     TaskMakeFile(MakeBase &par) : Task(par)
6692         { type = TASK_MAKEFILE; name = "makefile"; }
6694     virtual ~TaskMakeFile()
6695         {}
6697     virtual bool execute()
6698         {
6699         status("          : %s", fileName.c_str());
6700         String fullName = parent.resolve(fileName);
6701         if (!isNewerThan(parent.getURI().getPath(), fullName))
6702             {
6703             //trace("skipped <makefile>");
6704             return true;
6705             }
6706         //trace("fullName:%s", fullName.c_str());
6707         FILE *f = fopen(fullName.c_str(), "w");
6708         if (!f)
6709             {
6710             error("<makefile> could not open %s for writing : %s",
6711                 fullName.c_str(), strerror(errno));
6712             return false;
6713             }
6714         for (unsigned int i=0 ; i<text.size() ; i++)
6715             fputc(text[i], f);
6716         fputc('\n', f);
6717         fclose(f);
6718         return true;
6719         }
6721     virtual bool parse(Element *elem)
6722         {
6723         if (!parent.getAttribute(elem, "file", fileName))
6724             return false;
6725         if (fileName.size() == 0)
6726             {
6727             error("<makefile> requires 'file=\"filename\"' attribute");
6728             return false;
6729             }
6730         if (!parent.getValue(elem, text))
6731             return false;
6732         text = leftJustify(text);
6733         //trace("dirname:%s", dirName.c_str());
6734         return true;
6735         }
6737 private:
6739     String fileName;
6740     String text;
6741 };
6745 /**
6746  * Create a named directory
6747  */
6748 class TaskMkDir : public Task
6750 public:
6752     TaskMkDir(MakeBase &par) : Task(par)
6753         { type = TASK_MKDIR; name = "mkdir"; }
6755     virtual ~TaskMkDir()
6756         {}
6758     virtual bool execute()
6759         {
6760         status("          : %s", dirName.c_str());
6761         String fullDir = parent.resolve(dirName);
6762         //trace("fullDir:%s", fullDir.c_str());
6763         if (!createDirectory(fullDir))
6764             return false;
6765         return true;
6766         }
6768     virtual bool parse(Element *elem)
6769         {
6770         if (!parent.getAttribute(elem, "dir", dirName))
6771             return false;
6772         if (dirName.size() == 0)
6773             {
6774             error("<mkdir> requires 'dir=\"dirname\"' attribute");
6775             return false;
6776             }
6777         return true;
6778         }
6780 private:
6782     String dirName;
6783 };
6787 /**
6788  * Create a named directory
6789  */
6790 class TaskMsgFmt: public Task
6792 public:
6794     TaskMsgFmt(MakeBase &par) : Task(par)
6795          {
6796          type    = TASK_MSGFMT;
6797          name    = "msgfmt";
6798          command = "msgfmt";
6799          owndir  = false;
6800          outName = "";
6801          }
6803     virtual ~TaskMsgFmt()
6804         {}
6806     virtual bool execute()
6807         {
6808         if (!listFiles(parent, fileSet))
6809             return false;
6810         String fileSetDir = fileSet.getDirectory();
6812         //trace("msgfmt: %d", fileSet.size());
6813         for (unsigned int i=0 ; i<fileSet.size() ; i++)
6814             {
6815             String fileName = fileSet[i];
6816             if (getSuffix(fileName) != "po")
6817                 continue;
6818             String sourcePath;
6819             if (fileSetDir.size()>0)
6820                 {
6821                 sourcePath.append(fileSetDir);
6822                 sourcePath.append("/");
6823                 }
6824             sourcePath.append(fileName);
6825             String fullSource = parent.resolve(sourcePath);
6827             String destPath;
6828             if (toDirName.size()>0)
6829                 {
6830                 destPath.append(toDirName);
6831                 destPath.append("/");
6832                 }
6833             if (owndir)
6834                 {
6835                 String subdir = fileName;
6836                 unsigned int pos = subdir.find_last_of('.');
6837                 if (pos != subdir.npos)
6838                     subdir = subdir.substr(0, pos);
6839                 destPath.append(subdir);
6840                 destPath.append("/");
6841                 }
6842             //Pick the output file name
6843             if (outName.size() > 0)
6844                 {
6845                 destPath.append(outName);
6846                 }
6847             else
6848                 {
6849                 destPath.append(fileName);
6850                 destPath[destPath.size()-2] = 'm';
6851                 }
6853             String fullDest = parent.resolve(destPath);
6855             if (!isNewerThan(fullSource, fullDest))
6856                 {
6857                 //trace("skip %s", fullSource.c_str());
6858                 continue;
6859                 }
6860                 
6861             String cmd = command;
6862             cmd.append(" ");
6863             cmd.append(fullSource);
6864             cmd.append(" -o ");
6865             cmd.append(fullDest);
6866             
6867             int pos = fullDest.find_last_of('/');
6868             if (pos>0)
6869                 {
6870                 String fullDestPath = fullDest.substr(0, pos);
6871                 if (!createDirectory(fullDestPath))
6872                     return false;
6873                 }
6877             String outString, errString;
6878             if (!executeCommand(cmd.c_str(), "", outString, errString))
6879                 {
6880                 error("<msgfmt> problem: %s", errString.c_str());
6881                 return false;
6882                 }
6883             }
6885         return true;
6886         }
6888     virtual bool parse(Element *elem)
6889         {
6890         String s;
6891         if (!parent.getAttribute(elem, "command", s))
6892             return false;
6893         if (s.size()>0)
6894             command = s;
6895         if (!parent.getAttribute(elem, "todir", toDirName))
6896             return false;
6897         if (!parent.getAttribute(elem, "out", outName))
6898             return false;
6899         if (!parent.getAttribute(elem, "owndir", s))
6900             return false;
6901         if (s.size()>0 && !getBool(s, owndir))
6902             return false;
6903             
6904         std::vector<Element *> children = elem->getChildren();
6905         for (unsigned int i=0 ; i<children.size() ; i++)
6906             {
6907             Element *child = children[i];
6908             String tagName = child->getName();
6909             if (tagName == "fileset")
6910                 {
6911                 if (!parseFileSet(child, parent, fileSet))
6912                     return false;
6913                 }
6914             }
6915         return true;
6916         }
6918 private:
6920     String  command;
6921     String  toDirName;
6922     String  outName;
6923     FileSet fileSet;
6924     bool    owndir;
6926 };
6932 /**
6933  *  Process an archive to allow random access
6934  */
6935 class TaskRanlib : public Task
6937 public:
6939     TaskRanlib(MakeBase &par) : Task(par)
6940         {
6941         type = TASK_RANLIB; name = "ranlib";
6942         command = "ranlib";
6943         }
6945     virtual ~TaskRanlib()
6946         {}
6948     virtual bool execute()
6949         {
6950         String fullName = parent.resolve(fileName);
6951         //trace("fullDir:%s", fullDir.c_str());
6952         String cmd = command;
6953         cmd.append(" ");
6954         cmd.append(fullName);
6955         String outbuf, errbuf;
6956         if (!executeCommand(cmd, "", outbuf, errbuf))
6957             return false;
6958         return true;
6959         }
6961     virtual bool parse(Element *elem)
6962         {
6963         String s;
6964         if (!parent.getAttribute(elem, "command", s))
6965             return false;
6966         if (s.size()>0)
6967            command = s;
6968         if (!parent.getAttribute(elem, "file", fileName))
6969             return false;
6970         if (fileName.size() == 0)
6971             {
6972             error("<ranlib> requires 'file=\"fileNname\"' attribute");
6973             return false;
6974             }
6975         return true;
6976         }
6978 private:
6980     String fileName;
6981     String command;
6982 };
6986 /**
6987  * Run the "ar" command to archive .o's into a .a
6988  */
6989 class TaskRC : public Task
6991 public:
6993     TaskRC(MakeBase &par) : Task(par)
6994         {
6995         type = TASK_RC; name = "rc";
6996         command = "windres";
6997         }
6999     virtual ~TaskRC()
7000         {}
7002     virtual bool execute()
7003         {
7004         String fullFile = parent.resolve(fileName);
7005         String fullOut  = parent.resolve(outName);
7006         if (!isNewerThan(fullFile, fullOut))
7007             return true;
7008         String cmd = command;
7009         cmd.append(" -o ");
7010         cmd.append(fullOut);
7011         cmd.append(" ");
7012         cmd.append(flags);
7013         cmd.append(" ");
7014         cmd.append(fullFile);
7016         String outString, errString;
7017         if (!executeCommand(cmd.c_str(), "", outString, errString))
7018             {
7019             error("RC problem: %s", errString.c_str());
7020             return false;
7021             }
7022         return true;
7023         }
7025     virtual bool parse(Element *elem)
7026         {
7027         if (!parent.getAttribute(elem, "command", command))
7028             return false;
7029         if (!parent.getAttribute(elem, "file", fileName))
7030             return false;
7031         if (!parent.getAttribute(elem, "out", outName))
7032             return false;
7033         std::vector<Element *> children = elem->getChildren();
7034         for (unsigned int i=0 ; i<children.size() ; i++)
7035             {
7036             Element *child = children[i];
7037             String tagName = child->getName();
7038             if (tagName == "flags")
7039                 {
7040                 if (!parent.getValue(child, flags))
7041                     return false;
7042                 }
7043             }
7044         return true;
7045         }
7047 private:
7049     String command;
7050     String flags;
7051     String fileName;
7052     String outName;
7054 };
7058 /**
7059  *  Collect .o's into a .so or DLL
7060  */
7061 class TaskSharedLib : public Task
7063 public:
7065     TaskSharedLib(MakeBase &par) : Task(par)
7066         {
7067         type = TASK_SHAREDLIB; name = "dll";
7068         command = "dllwrap";
7069         }
7071     virtual ~TaskSharedLib()
7072         {}
7074     virtual bool execute()
7075         {
7076         //trace("###########HERE %d", fileSet.size());
7077         bool doit = false;
7078         
7079         String fullOut = parent.resolve(fileName);
7080         //trace("ar fullout: %s", fullOut.c_str());
7081         
7082         if (!listFiles(parent, fileSet))
7083             return false;
7084         String fileSetDir = fileSet.getDirectory();
7086         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7087             {
7088             String fname;
7089             if (fileSetDir.size()>0)
7090                 {
7091                 fname.append(fileSetDir);
7092                 fname.append("/");
7093                 }
7094             fname.append(fileSet[i]);
7095             String fullName = parent.resolve(fname);
7096             //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
7097             if (isNewerThan(fullName, fullOut))
7098                 doit = true;
7099             }
7100         //trace("Needs it:%d", doit);
7101         if (!doit)
7102             {
7103             return true;
7104             }
7106         String cmd = "dllwrap";
7107         cmd.append(" -o ");
7108         cmd.append(fullOut);
7109         if (defFileName.size()>0)
7110             {
7111             cmd.append(" --def ");
7112             cmd.append(defFileName);
7113             cmd.append(" ");
7114             }
7115         if (impFileName.size()>0)
7116             {
7117             cmd.append(" --implib ");
7118             cmd.append(impFileName);
7119             cmd.append(" ");
7120             }
7121         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7122             {
7123             String fname;
7124             if (fileSetDir.size()>0)
7125                 {
7126                 fname.append(fileSetDir);
7127                 fname.append("/");
7128                 }
7129             fname.append(fileSet[i]);
7130             String fullName = parent.resolve(fname);
7132             cmd.append(" ");
7133             cmd.append(fullName);
7134             }
7135         cmd.append(" ");
7136         cmd.append(libs);
7138         String outString, errString;
7139         if (!executeCommand(cmd.c_str(), "", outString, errString))
7140             {
7141             error("<sharedlib> problem: %s", errString.c_str());
7142             return false;
7143             }
7145         return true;
7146         }
7148     virtual bool parse(Element *elem)
7149         {
7150         if (!parent.getAttribute(elem, "file", fileName))
7151             return false;
7152         if (!parent.getAttribute(elem, "import", impFileName))
7153             return false;
7154         if (!parent.getAttribute(elem, "def", defFileName))
7155             return false;
7156             
7157         std::vector<Element *> children = elem->getChildren();
7158         for (unsigned int i=0 ; i<children.size() ; i++)
7159             {
7160             Element *child = children[i];
7161             String tagName = child->getName();
7162             if (tagName == "fileset")
7163                 {
7164                 if (!parseFileSet(child, parent, fileSet))
7165                     return false;
7166                 }
7167             else if (tagName == "libs")
7168                 {
7169                 if (!parent.getValue(child, libs))
7170                     return false;
7171                 libs = strip(libs);
7172                 }
7173             }
7174         return true;
7175         }
7177 private:
7179     String command;
7180     String fileName;
7181     String defFileName;
7182     String impFileName;
7183     FileSet fileSet;
7184     String libs;
7186 };
7190 /**
7191  * Run the "ar" command to archive .o's into a .a
7192  */
7193 class TaskStaticLib : public Task
7195 public:
7197     TaskStaticLib(MakeBase &par) : Task(par)
7198         {
7199         type = TASK_STATICLIB; name = "staticlib";
7200         command = "ar crv";
7201         }
7203     virtual ~TaskStaticLib()
7204         {}
7206     virtual bool execute()
7207         {
7208         //trace("###########HERE %d", fileSet.size());
7209         bool doit = false;
7210         
7211         String fullOut = parent.resolve(fileName);
7212         //trace("ar fullout: %s", fullOut.c_str());
7213         
7214         if (!listFiles(parent, fileSet))
7215             return false;
7216         String fileSetDir = fileSet.getDirectory();
7218         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7219             {
7220             String fname;
7221             if (fileSetDir.size()>0)
7222                 {
7223                 fname.append(fileSetDir);
7224                 fname.append("/");
7225                 }
7226             fname.append(fileSet[i]);
7227             String fullName = parent.resolve(fname);
7228             //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
7229             if (isNewerThan(fullName, fullOut))
7230                 doit = true;
7231             }
7232         //trace("Needs it:%d", doit);
7233         if (!doit)
7234             {
7235             return true;
7236             }
7238         String cmd = command;
7239         cmd.append(" ");
7240         cmd.append(fullOut);
7241         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7242             {
7243             String fname;
7244             if (fileSetDir.size()>0)
7245                 {
7246                 fname.append(fileSetDir);
7247                 fname.append("/");
7248                 }
7249             fname.append(fileSet[i]);
7250             String fullName = parent.resolve(fname);
7252             cmd.append(" ");
7253             cmd.append(fullName);
7254             }
7256         String outString, errString;
7257         if (!executeCommand(cmd.c_str(), "", outString, errString))
7258             {
7259             error("<staticlib> problem: %s", errString.c_str());
7260             return false;
7261             }
7263         return true;
7264         }
7267     virtual bool parse(Element *elem)
7268         {
7269         String s;
7270         if (!parent.getAttribute(elem, "command", s))
7271             return false;
7272         if (s.size()>0)
7273             command = s;
7274         if (!parent.getAttribute(elem, "file", fileName))
7275             return false;
7276             
7277         std::vector<Element *> children = elem->getChildren();
7278         for (unsigned int i=0 ; i<children.size() ; i++)
7279             {
7280             Element *child = children[i];
7281             String tagName = child->getName();
7282             if (tagName == "fileset")
7283                 {
7284                 if (!parseFileSet(child, parent, fileSet))
7285                     return false;
7286                 }
7287             }
7288         return true;
7289         }
7291 private:
7293     String command;
7294     String fileName;
7295     FileSet fileSet;
7297 };
7302 /**
7303  * Strip an executable
7304  */
7305 class TaskStrip : public Task
7307 public:
7309     TaskStrip(MakeBase &par) : Task(par)
7310         { type = TASK_STRIP; name = "strip"; }
7312     virtual ~TaskStrip()
7313         {}
7315     virtual bool execute()
7316         {
7317         String fullName = parent.resolve(fileName);
7318         //trace("fullDir:%s", fullDir.c_str());
7319         String cmd;
7320         String outbuf, errbuf;
7322         if (symFileName.size()>0)
7323             {
7324             String symFullName = parent.resolve(symFileName);
7325             cmd = "objcopy --only-keep-debug ";
7326             cmd.append(getNativePath(fullName));
7327             cmd.append(" ");
7328             cmd.append(getNativePath(symFullName));
7329             if (!executeCommand(cmd, "", outbuf, errbuf))
7330                 {
7331                 error("<strip> symbol file failed : %s", errbuf.c_str());
7332                 return false;
7333                 }
7334             }
7335             
7336         cmd = "strip ";
7337         cmd.append(getNativePath(fullName));
7338         if (!executeCommand(cmd, "", outbuf, errbuf))
7339             {
7340             error("<strip> failed : %s", errbuf.c_str());
7341             return false;
7342             }
7343         return true;
7344         }
7346     virtual bool parse(Element *elem)
7347         {
7348         if (!parent.getAttribute(elem, "file", fileName))
7349             return false;
7350         if (!parent.getAttribute(elem, "symfile", symFileName))
7351             return false;
7352         if (fileName.size() == 0)
7353             {
7354             error("<strip> requires 'file=\"fileName\"' attribute");
7355             return false;
7356             }
7357         return true;
7358         }
7360 private:
7362     String fileName;
7363     String symFileName;
7364 };
7367 /**
7368  *
7369  */
7370 class TaskTouch : public Task
7372 public:
7374     TaskTouch(MakeBase &par) : Task(par)
7375         { type = TASK_TOUCH; name = "touch"; }
7377     virtual ~TaskTouch()
7378         {}
7380     virtual bool execute()
7381         {
7382         String fullName = parent.resolve(fileName);
7383         String nativeFile = getNativePath(fullName);
7384         if (!isRegularFile(fullName) && !isDirectory(fullName))
7385             {            
7386             // S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH
7387             int ret = creat(nativeFile.c_str(), 0666);
7388             if (ret != 0) 
7389                 {
7390                 error("<touch> could not create '%s' : %s",
7391                     nativeFile.c_str(), strerror(ret));
7392                 return false;
7393                 }
7394             return true;
7395             }
7396         int ret = utime(nativeFile.c_str(), (struct utimbuf *)0);
7397         if (ret != 0)
7398             {
7399             error("<touch> could not update the modification time for '%s' : %s",
7400                 nativeFile.c_str(), strerror(ret));
7401             return false;
7402             }
7403         return true;
7404         }
7406     virtual bool parse(Element *elem)
7407         {
7408         //trace("touch parse");
7409         if (!parent.getAttribute(elem, "file", fileName))
7410             return false;
7411         if (fileName.size() == 0)
7412             {
7413             error("<touch> requires 'file=\"fileName\"' attribute");
7414             return false;
7415             }
7416         return true;
7417         }
7419     String fileName;
7420 };
7423 /**
7424  *
7425  */
7426 class TaskTstamp : public Task
7428 public:
7430     TaskTstamp(MakeBase &par) : Task(par)
7431         { type = TASK_TSTAMP; name = "tstamp"; }
7433     virtual ~TaskTstamp()
7434         {}
7436     virtual bool execute()
7437         {
7438         return true;
7439         }
7441     virtual bool parse(Element *elem)
7442         {
7443         //trace("tstamp parse");
7444         return true;
7445         }
7446 };
7450 /**
7451  *
7452  */
7453 Task *Task::createTask(Element *elem, int lineNr)
7455     String tagName = elem->getName();
7456     //trace("task:%s", tagName.c_str());
7457     Task *task = NULL;
7458     if (tagName == "cc")
7459         task = new TaskCC(parent);
7460     else if (tagName == "copy")
7461         task = new TaskCopy(parent);
7462     else if (tagName == "delete")
7463         task = new TaskDelete(parent);
7464     else if (tagName == "jar")
7465         task = new TaskJar(parent);
7466     else if (tagName == "javac")
7467         task = new TaskJavac(parent);
7468     else if (tagName == "link")
7469         task = new TaskLink(parent);
7470     else if (tagName == "makefile")
7471         task = new TaskMakeFile(parent);
7472     else if (tagName == "mkdir")
7473         task = new TaskMkDir(parent);
7474     else if (tagName == "msgfmt")
7475         task = new TaskMsgFmt(parent);
7476     else if (tagName == "ranlib")
7477         task = new TaskRanlib(parent);
7478     else if (tagName == "rc")
7479         task = new TaskRC(parent);
7480     else if (tagName == "sharedlib")
7481         task = new TaskSharedLib(parent);
7482     else if (tagName == "staticlib")
7483         task = new TaskStaticLib(parent);
7484     else if (tagName == "strip")
7485         task = new TaskStrip(parent);
7486     else if (tagName == "touch")
7487         task = new TaskTouch(parent);
7488     else if (tagName == "tstamp")
7489         task = new TaskTstamp(parent);
7490     else
7491         {
7492         error("Unknown task '%s'", tagName.c_str());
7493         return NULL;
7494         }
7496     task->setLine(lineNr);
7498     if (!task->parse(elem))
7499         {
7500         delete task;
7501         return NULL;
7502         }
7503     return task;
7508 //########################################################################
7509 //# T A R G E T
7510 //########################################################################
7512 /**
7513  *
7514  */
7515 class Target : public MakeBase
7518 public:
7520     /**
7521      *
7522      */
7523     Target(Make &par) : parent(par)
7524         { init(); }
7526     /**
7527      *
7528      */
7529     Target(const Target &other) : parent(other.parent)
7530         { init(); assign(other); }
7532     /**
7533      *
7534      */
7535     Target &operator=(const Target &other)
7536         { init(); assign(other); return *this; }
7538     /**
7539      *
7540      */
7541     virtual ~Target()
7542         { cleanup() ; }
7545     /**
7546      *
7547      */
7548     virtual Make &getParent()
7549         { return parent; }
7551     /**
7552      *
7553      */
7554     virtual String getName()
7555         { return name; }
7557     /**
7558      *
7559      */
7560     virtual void setName(const String &val)
7561         { name = val; }
7563     /**
7564      *
7565      */
7566     virtual String getDescription()
7567         { return description; }
7569     /**
7570      *
7571      */
7572     virtual void setDescription(const String &val)
7573         { description = val; }
7575     /**
7576      *
7577      */
7578     virtual void addDependency(const String &val)
7579         { deps.push_back(val); }
7581     /**
7582      *
7583      */
7584     virtual void parseDependencies(const String &val)
7585         { deps = tokenize(val, ", "); }
7587     /**
7588      *
7589      */
7590     virtual std::vector<String> &getDependencies()
7591         { return deps; }
7593     /**
7594      *
7595      */
7596     virtual String getIf()
7597         { return ifVar; }
7599     /**
7600      *
7601      */
7602     virtual void setIf(const String &val)
7603         { ifVar = val; }
7605     /**
7606      *
7607      */
7608     virtual String getUnless()
7609         { return unlessVar; }
7611     /**
7612      *
7613      */
7614     virtual void setUnless(const String &val)
7615         { unlessVar = val; }
7617     /**
7618      *
7619      */
7620     virtual void addTask(Task *val)
7621         { tasks.push_back(val); }
7623     /**
7624      *
7625      */
7626     virtual std::vector<Task *> &getTasks()
7627         { return tasks; }
7629 private:
7631     void init()
7632         {
7633         }
7635     void cleanup()
7636         {
7637         tasks.clear();
7638         }
7640     void assign(const Target &other)
7641         {
7642         //parent      = other.parent;
7643         name        = other.name;
7644         description = other.description;
7645         ifVar       = other.ifVar;
7646         unlessVar   = other.unlessVar;
7647         deps        = other.deps;
7648         tasks       = other.tasks;
7649         }
7651     Make &parent;
7653     String name;
7655     String description;
7657     String ifVar;
7659     String unlessVar;
7661     std::vector<String> deps;
7663     std::vector<Task *> tasks;
7665 };
7674 //########################################################################
7675 //# M A K E
7676 //########################################################################
7679 /**
7680  *
7681  */
7682 class Make : public MakeBase
7685 public:
7687     /**
7688      *
7689      */
7690     Make()
7691         { init(); }
7693     /**
7694      *
7695      */
7696     Make(const Make &other)
7697         { assign(other); }
7699     /**
7700      *
7701      */
7702     Make &operator=(const Make &other)
7703         { assign(other); return *this; }
7705     /**
7706      *
7707      */
7708     virtual ~Make()
7709         { cleanup(); }
7711     /**
7712      *
7713      */
7714     virtual std::map<String, Target> &getTargets()
7715         { return targets; }
7718     /**
7719      *
7720      */
7721     virtual String version()
7722         { return BUILDTOOL_VERSION; }
7724     /**
7725      * Overload a <property>
7726      */
7727     virtual bool specifyProperty(const String &name,
7728                                  const String &value);
7730     /**
7731      *
7732      */
7733     virtual bool run();
7735     /**
7736      *
7737      */
7738     virtual bool run(const String &target);
7742 private:
7744     /**
7745      *
7746      */
7747     void init();
7749     /**
7750      *
7751      */
7752     void cleanup();
7754     /**
7755      *
7756      */
7757     void assign(const Make &other);
7759     /**
7760      *
7761      */
7762     bool executeTask(Task &task);
7765     /**
7766      *
7767      */
7768     bool executeTarget(Target &target,
7769              std::set<String> &targetsCompleted);
7772     /**
7773      *
7774      */
7775     bool execute();
7777     /**
7778      *
7779      */
7780     bool checkTargetDependencies(Target &prop,
7781                     std::vector<String> &depList);
7783     /**
7784      *
7785      */
7786     bool parsePropertyFile(const String &fileName,
7787                            const String &prefix);
7789     /**
7790      *
7791      */
7792     bool parseProperty(Element *elem);
7794     /**
7795      *
7796      */
7797     bool parseFile();
7799     /**
7800      *
7801      */
7802     std::vector<String> glob(const String &pattern);
7805     //###############
7806     //# Fields
7807     //###############
7809     String projectName;
7811     String currentTarget;
7813     String defaultTarget;
7815     String specifiedTarget;
7817     String baseDir;
7819     String description;
7820     
7821     String envAlias;
7823     //std::vector<Property> properties;
7824     
7825     std::map<String, Target> targets;
7827     std::vector<Task *> allTasks;
7828     
7829     std::map<String, String> specifiedProperties;
7831 };
7834 //########################################################################
7835 //# C L A S S  M A I N T E N A N C E
7836 //########################################################################
7838 /**
7839  *
7840  */
7841 void Make::init()
7843     uri             = "build.xml";
7844     projectName     = "";
7845     currentTarget   = "";
7846     defaultTarget   = "";
7847     specifiedTarget = "";
7848     baseDir         = "";
7849     description     = "";
7850     envAlias        = "";
7851     properties.clear();
7852     for (unsigned int i = 0 ; i < allTasks.size() ; i++)
7853         delete allTasks[i];
7854     allTasks.clear();
7859 /**
7860  *
7861  */
7862 void Make::cleanup()
7864     for (unsigned int i = 0 ; i < allTasks.size() ; i++)
7865         delete allTasks[i];
7866     allTasks.clear();
7871 /**
7872  *
7873  */
7874 void Make::assign(const Make &other)
7876     uri              = other.uri;
7877     projectName      = other.projectName;
7878     currentTarget    = other.currentTarget;
7879     defaultTarget    = other.defaultTarget;
7880     specifiedTarget  = other.specifiedTarget;
7881     baseDir          = other.baseDir;
7882     description      = other.description;
7883     properties       = other.properties;
7888 //########################################################################
7889 //# U T I L I T Y    T A S K S
7890 //########################################################################
7892 /**
7893  *  Perform a file globbing
7894  */
7895 std::vector<String> Make::glob(const String &pattern)
7897     std::vector<String> res;
7898     return res;
7902 //########################################################################
7903 //# P U B L I C    A P I
7904 //########################################################################
7908 /**
7909  *
7910  */
7911 bool Make::executeTarget(Target &target,
7912              std::set<String> &targetsCompleted)
7915     String name = target.getName();
7917     //First get any dependencies for this target
7918     std::vector<String> deps = target.getDependencies();
7919     for (unsigned int i=0 ; i<deps.size() ; i++)
7920         {
7921         String dep = deps[i];
7922         //Did we do it already?  Skip
7923         if (targetsCompleted.find(dep)!=targetsCompleted.end())
7924             continue;
7925             
7926         std::map<String, Target> &tgts =
7927                target.getParent().getTargets();
7928         std::map<String, Target>::iterator iter =
7929                tgts.find(dep);
7930         if (iter == tgts.end())
7931             {
7932             error("Target '%s' dependency '%s' not found",
7933                       name.c_str(),  dep.c_str());
7934             return false;
7935             }
7936         Target depTarget = iter->second;
7937         if (!executeTarget(depTarget, targetsCompleted))
7938             {
7939             return false;
7940             }
7941         }
7943     status("## Target : %s", name.c_str());
7945     //Now let's do the tasks
7946     std::vector<Task *> &tasks = target.getTasks();
7947     for (unsigned int i=0 ; i<tasks.size() ; i++)
7948         {
7949         Task *task = tasks[i];
7950         status("---- task : %s", task->getName().c_str());
7951         if (!task->execute())
7952             {
7953             return false;
7954             }
7955         }
7956         
7957     targetsCompleted.insert(name);
7958     
7959     return true;
7964 /**
7965  *  Main execute() method.  Start here and work
7966  *  up the dependency tree 
7967  */
7968 bool Make::execute()
7970     status("######## EXECUTE");
7972     //Determine initial target
7973     if (specifiedTarget.size()>0)
7974         {
7975         currentTarget = specifiedTarget;
7976         }
7977     else if (defaultTarget.size()>0)
7978         {
7979         currentTarget = defaultTarget;
7980         }
7981     else
7982         {
7983         error("execute: no specified or default target requested");
7984         return false;
7985         }
7987     std::map<String, Target>::iterator iter =
7988                targets.find(currentTarget);
7989     if (iter == targets.end())
7990         {
7991         error("Initial target '%s' not found",
7992                  currentTarget.c_str());
7993         return false;
7994         }
7995         
7996     //Now run
7997     Target target = iter->second;
7998     std::set<String> targetsCompleted;
7999     if (!executeTarget(target, targetsCompleted))
8000         {
8001         return false;
8002         }
8004     status("######## EXECUTE COMPLETE");
8005     return true;
8011 /**
8012  *
8013  */
8014 bool Make::checkTargetDependencies(Target &target, 
8015                             std::vector<String> &depList)
8017     String tgtName = target.getName().c_str();
8018     depList.push_back(tgtName);
8020     std::vector<String> deps = target.getDependencies();
8021     for (unsigned int i=0 ; i<deps.size() ; i++)
8022         {
8023         String dep = deps[i];
8024         //First thing entered was the starting Target
8025         if (dep == depList[0])
8026             {
8027             error("Circular dependency '%s' found at '%s'",
8028                       dep.c_str(), tgtName.c_str());
8029             std::vector<String>::iterator diter;
8030             for (diter=depList.begin() ; diter!=depList.end() ; diter++)
8031                 {
8032                 error("  %s", diter->c_str());
8033                 }
8034             return false;
8035             }
8037         std::map<String, Target> &tgts =
8038                   target.getParent().getTargets();
8039         std::map<String, Target>::iterator titer = tgts.find(dep);
8040         if (titer == tgts.end())
8041             {
8042             error("Target '%s' dependency '%s' not found",
8043                       tgtName.c_str(), dep.c_str());
8044             return false;
8045             }
8046         if (!checkTargetDependencies(titer->second, depList))
8047             {
8048             return false;
8049             }
8050         }
8051     return true;
8058 static int getword(int pos, const String &inbuf, String &result)
8060     int p = pos;
8061     int len = (int)inbuf.size();
8062     String val;
8063     while (p < len)
8064         {
8065         char ch = inbuf[p];
8066         if (!isalnum(ch) && ch!='.' && ch!='_')
8067             break;
8068         val.push_back(ch);
8069         p++;
8070         }
8071     result = val;
8072     return p;
8078 /**
8079  *
8080  */
8081 bool Make::parsePropertyFile(const String &fileName,
8082                              const String &prefix)
8084     FILE *f = fopen(fileName.c_str(), "r");
8085     if (!f)
8086         {
8087         error("could not open property file %s", fileName.c_str());
8088         return false;
8089         }
8090     int linenr = 0;
8091     while (!feof(f))
8092         {
8093         char buf[256];
8094         if (!fgets(buf, 255, f))
8095             break;
8096         linenr++;
8097         String s = buf;
8098         s = trim(s);
8099         int len = s.size();
8100         if (len == 0)
8101             continue;
8102         if (s[0] == '#')
8103             continue;
8104         String key;
8105         String val;
8106         int p = 0;
8107         int p2 = getword(p, s, key);
8108         if (p2 <= p)
8109             {
8110             error("property file %s, line %d: expected keyword",
8111                     fileName.c_str(), linenr);
8112             return false;
8113             }
8114         if (prefix.size() > 0)
8115             {
8116             key.insert(0, prefix);
8117             }
8119         //skip whitespace
8120         for (p=p2 ; p<len ; p++)
8121             if (!isspace(s[p]))
8122                 break;
8124         if (p>=len || s[p]!='=')
8125             {
8126             error("property file %s, line %d: expected '='",
8127                     fileName.c_str(), linenr);
8128             return false;
8129             }
8130         p++;
8132         //skip whitespace
8133         for ( ; p<len ; p++)
8134             if (!isspace(s[p]))
8135                 break;
8137         /* This way expects a word after the =
8138         p2 = getword(p, s, val);
8139         if (p2 <= p)
8140             {
8141             error("property file %s, line %d: expected value",
8142                     fileName.c_str(), linenr);
8143             return false;
8144             }
8145         */
8146         // This way gets the rest of the line after the =
8147         if (p>=len)
8148             {
8149             error("property file %s, line %d: expected value",
8150                     fileName.c_str(), linenr);
8151             return false;
8152             }
8153         val = s.substr(p);
8154         if (key.size()==0)
8155             continue;
8156         //allow property to be set, even if val=""
8158         //trace("key:'%s' val:'%s'", key.c_str(), val.c_str());
8159         //See if we wanted to overload this property
8160         std::map<String, String>::iterator iter =
8161             specifiedProperties.find(key);
8162         if (iter!=specifiedProperties.end())
8163             {
8164             val = iter->second;
8165             status("overloading property '%s' = '%s'",
8166                    key.c_str(), val.c_str());
8167             }
8168         properties[key] = val;
8169         }
8170     fclose(f);
8171     return true;
8177 /**
8178  *
8179  */
8180 bool Make::parseProperty(Element *elem)
8182     std::vector<Attribute> &attrs = elem->getAttributes();
8183     for (unsigned int i=0 ; i<attrs.size() ; i++)
8184         {
8185         String attrName = attrs[i].getName();
8186         String attrVal  = attrs[i].getValue();
8188         if (attrName == "name")
8189             {
8190             String val;
8191             if (!getAttribute(elem, "value", val))
8192                 return false;
8193             if (val.size() > 0)
8194                 {
8195                 properties[attrVal] = val;
8196                 }
8197             else
8198                 {
8199                 if (!getAttribute(elem, "location", val))
8200                     return false;
8201                 //let the property exist, even if not defined
8202                 properties[attrVal] = val;
8203                 }
8204             //See if we wanted to overload this property
8205             std::map<String, String>::iterator iter =
8206                 specifiedProperties.find(attrVal);
8207             if (iter != specifiedProperties.end())
8208                 {
8209                 val = iter->second;
8210                 status("overloading property '%s' = '%s'",
8211                     attrVal.c_str(), val.c_str());
8212                 properties[attrVal] = val;
8213                 }
8214             }
8215         else if (attrName == "file")
8216             {
8217             String prefix;
8218             if (!getAttribute(elem, "prefix", prefix))
8219                 return false;
8220             if (prefix.size() > 0)
8221                 {
8222                 if (prefix[prefix.size()-1] != '.')
8223                     prefix.push_back('.');
8224                 }
8225             if (!parsePropertyFile(attrName, prefix))
8226                 return false;
8227             }
8228         else if (attrName == "environment")
8229             {
8230             if (envAlias.size() > 0)
8231                 {
8232                 error("environment property can only be set once");
8233                 return false;
8234                 }
8235             envAlias = attrVal;
8236             }
8237         }
8239     return true;
8245 /**
8246  *
8247  */
8248 bool Make::parseFile()
8250     status("######## PARSE : %s", uri.getPath().c_str());
8252     setLine(0);
8254     Parser parser;
8255     Element *root = parser.parseFile(uri.getNativePath());
8256     if (!root)
8257         {
8258         error("Could not open %s for reading",
8259               uri.getNativePath().c_str());
8260         return false;
8261         }
8262     
8263     setLine(root->getLine());
8265     if (root->getChildren().size()==0 ||
8266         root->getChildren()[0]->getName()!="project")
8267         {
8268         error("Main xml element should be <project>");
8269         delete root;
8270         return false;
8271         }
8273     //########## Project attributes
8274     Element *project = root->getChildren()[0];
8275     String s = project->getAttribute("name");
8276     if (s.size() > 0)
8277         projectName = s;
8278     s = project->getAttribute("default");
8279     if (s.size() > 0)
8280         defaultTarget = s;
8281     s = project->getAttribute("basedir");
8282     if (s.size() > 0)
8283         baseDir = s;
8285     //######### PARSE MEMBERS
8286     std::vector<Element *> children = project->getChildren();
8287     for (unsigned int i=0 ; i<children.size() ; i++)
8288         {
8289         Element *elem = children[i];
8290         setLine(elem->getLine());
8291         String tagName = elem->getName();
8293         //########## DESCRIPTION
8294         if (tagName == "description")
8295             {
8296             description = parser.trim(elem->getValue());
8297             }
8299         //######### PROPERTY
8300         else if (tagName == "property")
8301             {
8302             if (!parseProperty(elem))
8303                 return false;
8304             }
8306         //######### TARGET
8307         else if (tagName == "target")
8308             {
8309             String tname   = elem->getAttribute("name");
8310             String tdesc   = elem->getAttribute("description");
8311             String tdeps   = elem->getAttribute("depends");
8312             String tif     = elem->getAttribute("if");
8313             String tunless = elem->getAttribute("unless");
8314             Target target(*this);
8315             target.setName(tname);
8316             target.setDescription(tdesc);
8317             target.parseDependencies(tdeps);
8318             target.setIf(tif);
8319             target.setUnless(tunless);
8320             std::vector<Element *> telems = elem->getChildren();
8321             for (unsigned int i=0 ; i<telems.size() ; i++)
8322                 {
8323                 Element *telem = telems[i];
8324                 Task breeder(*this);
8325                 Task *task = breeder.createTask(telem, telem->getLine());
8326                 if (!task)
8327                     return false;
8328                 allTasks.push_back(task);
8329                 target.addTask(task);
8330                 }
8332             //Check name
8333             if (tname.size() == 0)
8334                 {
8335                 error("no name for target");
8336                 return false;
8337                 }
8338             //Check for duplicate name
8339             if (targets.find(tname) != targets.end())
8340                 {
8341                 error("target '%s' already defined", tname.c_str());
8342                 return false;
8343                 }
8344             //more work than targets[tname]=target, but avoids default allocator
8345             targets.insert(std::make_pair<String, Target>(tname, target));
8346             }
8347         //######### none of the above
8348         else
8349             {
8350             error("unknown toplevel tag: <%s>", tagName.c_str());
8351             return false;
8352             }
8354         }
8356     std::map<String, Target>::iterator iter;
8357     for (iter = targets.begin() ; iter!= targets.end() ; iter++)
8358         {
8359         Target tgt = iter->second;
8360         std::vector<String> depList;
8361         if (!checkTargetDependencies(tgt, depList))
8362             {
8363             return false;
8364             }
8365         }
8368     delete root;
8369     status("######## PARSE COMPLETE");
8370     return true;
8374 /**
8375  * Overload a <property>
8376  */
8377 bool Make::specifyProperty(const String &name, const String &value)
8379     if (specifiedProperties.find(name) != specifiedProperties.end())
8380         {
8381         error("Property %s already specified", name.c_str());
8382         return false;
8383         }
8384     specifiedProperties[name] = value;
8385     return true;
8390 /**
8391  *
8392  */
8393 bool Make::run()
8395     if (!parseFile())
8396         return false;
8397         
8398     if (!execute())
8399         return false;
8401     return true;
8407 /**
8408  * Get a formatted MM:SS.sss time elapsed string
8409  */ 
8410 static String
8411 timeDiffString(struct timeval &x, struct timeval &y)
8413     long microsX  = x.tv_usec;
8414     long secondsX = x.tv_sec;
8415     long microsY  = y.tv_usec;
8416     long secondsY = y.tv_sec;
8417     if (microsX < microsY)
8418         {
8419         microsX += 1000000;
8420         secondsX -= 1;
8421         }
8423     int seconds = (int)(secondsX - secondsY);
8424     int millis  = (int)((microsX - microsY)/1000);
8426     int minutes = seconds/60;
8427     seconds -= minutes*60;
8428     char buf[80];
8429     snprintf(buf, 79, "%dm %d.%03ds", minutes, seconds, millis);
8430     String ret = buf;
8431     return ret;
8432     
8435 /**
8436  *
8437  */
8438 bool Make::run(const String &target)
8440     status("####################################################");
8441     status("#   %s", version().c_str());
8442     status("####################################################");
8443     struct timeval timeStart, timeEnd;
8444     ::gettimeofday(&timeStart, NULL);
8445     specifiedTarget = target;
8446     if (!run())
8447         return false;
8448     ::gettimeofday(&timeEnd, NULL);
8449     String timeStr = timeDiffString(timeEnd, timeStart);
8450     status("####################################################");
8451     status("#   BuildTool Completed : %s", timeStr.c_str());
8452     status("####################################################");
8453     return true;
8462 }// namespace buildtool
8463 //########################################################################
8464 //# M A I N
8465 //########################################################################
8467 typedef buildtool::String String;
8469 /**
8470  *  Format an error message in printf() style
8471  */
8472 static void error(char *fmt, ...)
8474     va_list ap;
8475     va_start(ap, fmt);
8476     fprintf(stderr, "BuildTool error: ");
8477     vfprintf(stderr, fmt, ap);
8478     fprintf(stderr, "\n");
8479     va_end(ap);
8483 static bool parseProperty(const String &s, String &name, String &val)
8485     int len = s.size();
8486     int i;
8487     for (i=0 ; i<len ; i++)
8488         {
8489         char ch = s[i];
8490         if (ch == '=')
8491             break;
8492         name.push_back(ch);
8493         }
8494     if (i>=len || s[i]!='=')
8495         {
8496         error("property requires -Dname=value");
8497         return false;
8498         }
8499     i++;
8500     for ( ; i<len ; i++)
8501         {
8502         char ch = s[i];
8503         val.push_back(ch);
8504         }
8505     return true;
8509 /**
8510  * Compare a buffer with a key, for the length of the key
8511  */
8512 static bool sequ(const String &buf, char *key)
8514     int len = buf.size();
8515     for (int i=0 ; key[i] && i<len ; i++)
8516         {
8517         if (key[i] != buf[i])
8518             return false;
8519         }        
8520     return true;
8523 static void usage(int argc, char **argv)
8525     printf("usage:\n");
8526     printf("   %s [options] [target]\n", argv[0]);
8527     printf("Options:\n");
8528     printf("  -help, -h              print this message\n");
8529     printf("  -version               print the version information and exit\n");
8530     printf("  -file <file>           use given buildfile\n");
8531     printf("  -f <file>                 ''\n");
8532     printf("  -D<property>=<value>   use value for given property\n");
8538 /**
8539  * Parse the command-line args, get our options,
8540  * and run this thing
8541  */   
8542 static bool parseOptions(int argc, char **argv)
8544     if (argc < 1)
8545         {
8546         error("Cannot parse arguments");
8547         return false;
8548         }
8550     buildtool::Make make;
8552     String target;
8554     //char *progName = argv[0];
8555     for (int i=1 ; i<argc ; i++)
8556         {
8557         String arg = argv[i];
8558         if (arg.size()>1 && arg[0]=='-')
8559             {
8560             if (arg == "-h" || arg == "-help")
8561                 {
8562                 usage(argc,argv);
8563                 return true;
8564                 }
8565             else if (arg == "-version")
8566                 {
8567                 printf("%s", make.version().c_str());
8568                 return true;
8569                 }
8570             else if (arg == "-f" || arg == "-file")
8571                 {
8572                 if (i>=argc)
8573                    {
8574                    usage(argc, argv);
8575                    return false;
8576                    }
8577                 i++; //eat option
8578                 make.setURI(argv[i]);
8579                 }
8580             else if (arg.size()>2 && sequ(arg, "-D"))
8581                 {
8582                 String s = arg.substr(2, s.size());
8583                 String name, value;
8584                 if (!parseProperty(s, name, value))
8585                    {
8586                    usage(argc, argv);
8587                    return false;
8588                    }
8589                 if (!make.specifyProperty(name, value))
8590                     return false;
8591                 }
8592             else
8593                 {
8594                 error("Unknown option:%s", arg.c_str());
8595                 return false;
8596                 }
8597             }
8598         else
8599             {
8600             if (target.size()>0)
8601                 {
8602                 error("only one initial target");
8603                 usage(argc, argv);
8604                 return false;
8605                 }
8606             target = arg;
8607             }
8608         }
8610     //We have the options.  Now execute them
8611     if (!make.run(target))
8612         return false;
8614     return true;
8620 /*
8621 static bool runMake()
8623     buildtool::Make make;
8624     if (!make.run())
8625         return false;
8626     return true;
8630 static bool pkgConfigTest()
8632     buildtool::PkgConfig pkgConfig;
8633     if (!pkgConfig.readFile("gtk+-2.0.pc"))
8634         return false;
8635     return true;
8640 static bool depTest()
8642     buildtool::DepTool deptool;
8643     deptool.setSourceDirectory("/dev/ink/inkscape/src");
8644     if (!deptool.generateDependencies("build.dep"))
8645         return false;
8646     std::vector<buildtool::FileRec> res =
8647            deptool.loadDepFile("build.dep");
8648     if (res.size() == 0)
8649         return false;
8650     return true;
8653 static bool popenTest()
8655     buildtool::Make make;
8656     buildtool::String out, err;
8657     bool ret = make.executeCommand("gcc xx.cpp", "", out, err);
8658     printf("Popen test:%d '%s' '%s'\n", ret, out.c_str(), err.c_str());
8659     return true;
8663 static bool propFileTest()
8665     buildtool::Make make;
8666     make.parsePropertyFile("test.prop", "test.");
8667     return true;
8669 */
8671 int main(int argc, char **argv)
8674     if (!parseOptions(argc, argv))
8675         return 1;
8676     /*
8677     if (!popenTest())
8678         return 1;
8680     if (!depTest())
8681         return 1;
8682     if (!propFileTest())
8683         return 1;
8684     if (runMake())
8685         return 1;
8686     */
8687     return 0;
8691 //########################################################################
8692 //# E N D 
8693 //########################################################################