Code

remove debug messages that sneaked in
[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: recent win32api builds from MinGW have gettimeofday()
35  * defined, so you might need to build with 
36  * g++ -O3 -DHAVE_GETTIMEOFDAY buildtool.cpp -o btool.exe
37  *     
38  */  
40 #define BUILDTOOL_VERSION  "BuildTool v0.6.5, 2007 Bob Jamison"
42 #include <stdio.h>
43 #include <unistd.h>
44 #include <stdarg.h>
45 #include <sys/stat.h>
46 #include <time.h>
47 #include <sys/time.h>
48 #include <utime.h>
49 #include <dirent.h>
51 #include <string>
52 #include <map>
53 #include <set>
54 #include <vector>
56 #ifdef __WIN32__
57 #include <windows.h>
58 #endif
61 #include <errno.h>
64 //########################################################################
65 //# Definition of gettimeofday() for those who don't have it
66 //########################################################################
67 #ifndef HAVE_GETTIMEOFDAY
68 #include <sys/timeb.h>
70 struct timezone {
71       int tz_minuteswest; /* minutes west of Greenwich */
72       int tz_dsttime;     /* type of dst correction */
73     };
75 static int gettimeofday (struct timeval *tv, struct timezone *tz)
76 {
77    struct _timeb tb;
79    if (!tv)
80       return (-1);
82     _ftime (&tb);
83     tv->tv_sec  = tb.time;
84     tv->tv_usec = tb.millitm * 1000 + 500;
85     if (tz)
86         {
87         tz->tz_minuteswest = -60 * _timezone;
88         tz->tz_dsttime = _daylight;
89         }
90     return 0;
91 }
93 #endif
101 namespace buildtool
107 //########################################################################
108 //########################################################################
109 //##  R E G E X P
110 //########################################################################
111 //########################################################################
113 /**
114  * This is the T-Rex regular expression library, which we
115  * gratefully acknowledge.  It's clean code and small size allow
116  * us to embed it in BuildTool without adding a dependency
117  *
118  */    
120 //begin trex.h
122 #ifndef _TREX_H_
123 #define _TREX_H_
124 /***************************************************************
125     T-Rex a tiny regular expression library
127     Copyright (C) 2003-2006 Alberto Demichelis
129     This software is provided 'as-is', without any express 
130     or implied warranty. In no event will the authors be held 
131     liable for any damages arising from the use of this software.
133     Permission is granted to anyone to use this software for 
134     any purpose, including commercial applications, and to alter
135     it and redistribute it freely, subject to the following restrictions:
137         1. The origin of this software must not be misrepresented;
138         you must not claim that you wrote the original software.
139         If you use this software in a product, an acknowledgment
140         in the product documentation would be appreciated but
141         is not required.
143         2. Altered source versions must be plainly marked as such,
144         and must not be misrepresented as being the original software.
146         3. This notice may not be removed or altered from any
147         source distribution.
149 ****************************************************************/
151 #ifdef _UNICODE
152 #define TRexChar unsigned short
153 #define MAX_CHAR 0xFFFF
154 #define _TREXC(c) L##c 
155 #define trex_strlen wcslen
156 #define trex_printf wprintf
157 #else
158 #define TRexChar char
159 #define MAX_CHAR 0xFF
160 #define _TREXC(c) (c) 
161 #define trex_strlen strlen
162 #define trex_printf printf
163 #endif
165 #ifndef TREX_API
166 #define TREX_API extern
167 #endif
169 #define TRex_True 1
170 #define TRex_False 0
172 typedef unsigned int TRexBool;
173 typedef struct TRex TRex;
175 typedef struct {
176     const TRexChar *begin;
177     int len;
178 } TRexMatch;
180 TREX_API TRex *trex_compile(const TRexChar *pattern,const TRexChar **error);
181 TREX_API void trex_free(TRex *exp);
182 TREX_API TRexBool trex_match(TRex* exp,const TRexChar* text);
183 TREX_API TRexBool trex_search(TRex* exp,const TRexChar* text, const TRexChar** out_begin, const TRexChar** out_end);
184 TREX_API TRexBool trex_searchrange(TRex* exp,const TRexChar* text_begin,const TRexChar* text_end,const TRexChar** out_begin, const TRexChar** out_end);
185 TREX_API int trex_getsubexpcount(TRex* exp);
186 TREX_API TRexBool trex_getsubexp(TRex* exp, int n, TRexMatch *subexp);
188 #endif
190 //end trex.h
192 //start trex.c
195 #include <stdio.h>
196 #include <string>
198 /* see copyright notice in trex.h */
199 #include <string.h>
200 #include <stdlib.h>
201 #include <ctype.h>
202 #include <setjmp.h>
203 //#include "trex.h"
205 #ifdef _UINCODE
206 #define scisprint iswprint
207 #define scstrlen wcslen
208 #define scprintf wprintf
209 #define _SC(x) L(x)
210 #else
211 #define scisprint isprint
212 #define scstrlen strlen
213 #define scprintf printf
214 #define _SC(x) (x)
215 #endif
217 #ifdef _DEBUG
218 #include <stdio.h>
220 static const TRexChar *g_nnames[] =
222     _SC("NONE"),_SC("OP_GREEDY"),    _SC("OP_OR"),
223     _SC("OP_EXPR"),_SC("OP_NOCAPEXPR"),_SC("OP_DOT"),    _SC("OP_CLASS"),
224     _SC("OP_CCLASS"),_SC("OP_NCLASS"),_SC("OP_RANGE"),_SC("OP_CHAR"),
225     _SC("OP_EOL"),_SC("OP_BOL"),_SC("OP_WB")
226 };
228 #endif
229 #define OP_GREEDY        (MAX_CHAR+1) // * + ? {n}
230 #define OP_OR            (MAX_CHAR+2)
231 #define OP_EXPR            (MAX_CHAR+3) //parentesis ()
232 #define OP_NOCAPEXPR    (MAX_CHAR+4) //parentesis (?:)
233 #define OP_DOT            (MAX_CHAR+5)
234 #define OP_CLASS        (MAX_CHAR+6)
235 #define OP_CCLASS        (MAX_CHAR+7)
236 #define OP_NCLASS        (MAX_CHAR+8) //negates class the [^
237 #define OP_RANGE        (MAX_CHAR+9)
238 #define OP_CHAR            (MAX_CHAR+10)
239 #define OP_EOL            (MAX_CHAR+11)
240 #define OP_BOL            (MAX_CHAR+12)
241 #define OP_WB            (MAX_CHAR+13)
243 #define TREX_SYMBOL_ANY_CHAR ('.')
244 #define TREX_SYMBOL_GREEDY_ONE_OR_MORE ('+')
245 #define TREX_SYMBOL_GREEDY_ZERO_OR_MORE ('*')
246 #define TREX_SYMBOL_GREEDY_ZERO_OR_ONE ('?')
247 #define TREX_SYMBOL_BRANCH ('|')
248 #define TREX_SYMBOL_END_OF_STRING ('$')
249 #define TREX_SYMBOL_BEGINNING_OF_STRING ('^')
250 #define TREX_SYMBOL_ESCAPE_CHAR ('\\')
253 typedef int TRexNodeType;
255 typedef struct tagTRexNode{
256     TRexNodeType type;
257     int left;
258     int right;
259     int next;
260 }TRexNode;
262 struct TRex{
263     const TRexChar *_eol;
264     const TRexChar *_bol;
265     const TRexChar *_p;
266     int _first;
267     int _op;
268     TRexNode *_nodes;
269     int _nallocated;
270     int _nsize;
271     int _nsubexpr;
272     TRexMatch *_matches;
273     int _currsubexp;
274     void *_jmpbuf;
275     const TRexChar **_error;
276 };
278 static int trex_list(TRex *exp);
280 static int trex_newnode(TRex *exp, TRexNodeType type)
282     TRexNode n;
283     int newid;
284     n.type = type;
285     n.next = n.right = n.left = -1;
286     if(type == OP_EXPR)
287         n.right = exp->_nsubexpr++;
288     if(exp->_nallocated < (exp->_nsize + 1)) {
289         //int oldsize = exp->_nallocated;
290         exp->_nallocated *= 2;
291         exp->_nodes = (TRexNode *)realloc(exp->_nodes, exp->_nallocated * sizeof(TRexNode));
292     }
293     exp->_nodes[exp->_nsize++] = n;
294     newid = exp->_nsize - 1;
295     return (int)newid;
298 static void trex_error(TRex *exp,const TRexChar *error)
300     if(exp->_error) *exp->_error = error;
301     longjmp(*((jmp_buf*)exp->_jmpbuf),-1);
304 static void trex_expect(TRex *exp, int n){
305     if((*exp->_p) != n) 
306         trex_error(exp, _SC("expected paren"));
307     exp->_p++;
310 static TRexChar trex_escapechar(TRex *exp)
312     if(*exp->_p == TREX_SYMBOL_ESCAPE_CHAR){
313         exp->_p++;
314         switch(*exp->_p) {
315         case 'v': exp->_p++; return '\v';
316         case 'n': exp->_p++; return '\n';
317         case 't': exp->_p++; return '\t';
318         case 'r': exp->_p++; return '\r';
319         case 'f': exp->_p++; return '\f';
320         default: return (*exp->_p++);
321         }
322     } else if(!scisprint(*exp->_p)) trex_error(exp,_SC("letter expected"));
323     return (*exp->_p++);
326 static int trex_charclass(TRex *exp,int classid)
328     int n = trex_newnode(exp,OP_CCLASS);
329     exp->_nodes[n].left = classid;
330     return n;
333 static int trex_charnode(TRex *exp,TRexBool isclass)
335     TRexChar t;
336     if(*exp->_p == TREX_SYMBOL_ESCAPE_CHAR) {
337         exp->_p++;
338         switch(*exp->_p) {
339             case 'n': exp->_p++; return trex_newnode(exp,'\n');
340             case 't': exp->_p++; return trex_newnode(exp,'\t');
341             case 'r': exp->_p++; return trex_newnode(exp,'\r');
342             case 'f': exp->_p++; return trex_newnode(exp,'\f');
343             case 'v': exp->_p++; return trex_newnode(exp,'\v');
344             case 'a': case 'A': case 'w': case 'W': case 's': case 'S': 
345             case 'd': case 'D': case 'x': case 'X': case 'c': case 'C': 
346             case 'p': case 'P': case 'l': case 'u': 
347                 {
348                 t = *exp->_p; exp->_p++; 
349                 return trex_charclass(exp,t);
350                 }
351             case 'b': 
352             case 'B':
353                 if(!isclass) {
354                     int node = trex_newnode(exp,OP_WB);
355                     exp->_nodes[node].left = *exp->_p;
356                     exp->_p++; 
357                     return node;
358                 } //else default
359             default: 
360                 t = *exp->_p; exp->_p++; 
361                 return trex_newnode(exp,t);
362         }
363     }
364     else if(!scisprint(*exp->_p)) {
365         
366         trex_error(exp,_SC("letter expected"));
367     }
368     t = *exp->_p; exp->_p++; 
369     return trex_newnode(exp,t);
371 static int trex_class(TRex *exp)
373     int ret = -1;
374     int first = -1,chain;
375     if(*exp->_p == TREX_SYMBOL_BEGINNING_OF_STRING){
376         ret = trex_newnode(exp,OP_NCLASS);
377         exp->_p++;
378     }else ret = trex_newnode(exp,OP_CLASS);
379     
380     if(*exp->_p == ']') trex_error(exp,_SC("empty class"));
381     chain = ret;
382     while(*exp->_p != ']' && exp->_p != exp->_eol) {
383         if(*exp->_p == '-' && first != -1){ 
384             int r,t;
385             if(*exp->_p++ == ']') trex_error(exp,_SC("unfinished range"));
386             r = trex_newnode(exp,OP_RANGE);
387             if(first>*exp->_p) trex_error(exp,_SC("invalid range"));
388             if(exp->_nodes[first].type == OP_CCLASS) trex_error(exp,_SC("cannot use character classes in ranges"));
389             exp->_nodes[r].left = exp->_nodes[first].type;
390             t = trex_escapechar(exp);
391             exp->_nodes[r].right = t;
392             exp->_nodes[chain].next = r;
393             chain = r;
394             first = -1;
395         }
396         else{
397             if(first!=-1){
398                 int c = first;
399                 exp->_nodes[chain].next = c;
400                 chain = c;
401                 first = trex_charnode(exp,TRex_True);
402             }
403             else{
404                 first = trex_charnode(exp,TRex_True);
405             }
406         }
407     }
408     if(first!=-1){
409         int c = first;
410         exp->_nodes[chain].next = c;
411         chain = c;
412         first = -1;
413     }
414     /* hack? */
415     exp->_nodes[ret].left = exp->_nodes[ret].next;
416     exp->_nodes[ret].next = -1;
417     return ret;
420 static int trex_parsenumber(TRex *exp)
422     int ret = *exp->_p-'0';
423     int positions = 10;
424     exp->_p++;
425     while(isdigit(*exp->_p)) {
426         ret = ret*10+(*exp->_p++-'0');
427         if(positions==1000000000) trex_error(exp,_SC("overflow in numeric constant"));
428         positions *= 10;
429     };
430     return ret;
433 static int trex_element(TRex *exp)
435     int ret = -1;
436     switch(*exp->_p)
437     {
438     case '(': {
439         int expr,newn;
440         exp->_p++;
443         if(*exp->_p =='?') {
444             exp->_p++;
445             trex_expect(exp,':');
446             expr = trex_newnode(exp,OP_NOCAPEXPR);
447         }
448         else
449             expr = trex_newnode(exp,OP_EXPR);
450         newn = trex_list(exp);
451         exp->_nodes[expr].left = newn;
452         ret = expr;
453         trex_expect(exp,')');
454               }
455               break;
456     case '[':
457         exp->_p++;
458         ret = trex_class(exp);
459         trex_expect(exp,']');
460         break;
461     case TREX_SYMBOL_END_OF_STRING: exp->_p++; ret = trex_newnode(exp,OP_EOL);break;
462     case TREX_SYMBOL_ANY_CHAR: exp->_p++; ret = trex_newnode(exp,OP_DOT);break;
463     default:
464         ret = trex_charnode(exp,TRex_False);
465         break;
466     }
468     {
469         int op;
470         TRexBool isgreedy = TRex_False;
471         unsigned short p0 = 0, p1 = 0;
472         switch(*exp->_p){
473             case TREX_SYMBOL_GREEDY_ZERO_OR_MORE: p0 = 0; p1 = 0xFFFF; exp->_p++; isgreedy = TRex_True; break;
474             case TREX_SYMBOL_GREEDY_ONE_OR_MORE: p0 = 1; p1 = 0xFFFF; exp->_p++; isgreedy = TRex_True; break;
475             case TREX_SYMBOL_GREEDY_ZERO_OR_ONE: p0 = 0; p1 = 1; exp->_p++; isgreedy = TRex_True; break;
476             case '{':
477                 exp->_p++;
478                 if(!isdigit(*exp->_p)) trex_error(exp,_SC("number expected"));
479                 p0 = (unsigned short)trex_parsenumber(exp);
480                 /*******************************/
481                 switch(*exp->_p) {
482             case '}':
483                 p1 = p0; exp->_p++;
484                 break;
485             case ',':
486                 exp->_p++;
487                 p1 = 0xFFFF;
488                 if(isdigit(*exp->_p)){
489                     p1 = (unsigned short)trex_parsenumber(exp);
490                 }
491                 trex_expect(exp,'}');
492                 break;
493             default:
494                 trex_error(exp,_SC(", or } expected"));
495         }
496         /*******************************/
497         isgreedy = TRex_True; 
498         break;
500         }
501         if(isgreedy) {
502             int nnode = trex_newnode(exp,OP_GREEDY);
503             op = OP_GREEDY;
504             exp->_nodes[nnode].left = ret;
505             exp->_nodes[nnode].right = ((p0)<<16)|p1;
506             ret = nnode;
507         }
508     }
509     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')) {
510         int nnode = trex_element(exp);
511         exp->_nodes[ret].next = nnode;
512     }
514     return ret;
517 static int trex_list(TRex *exp)
519     int ret=-1,e;
520     if(*exp->_p == TREX_SYMBOL_BEGINNING_OF_STRING) {
521         exp->_p++;
522         ret = trex_newnode(exp,OP_BOL);
523     }
524     e = trex_element(exp);
525     if(ret != -1) {
526         exp->_nodes[ret].next = e;
527     }
528     else ret = e;
530     if(*exp->_p == TREX_SYMBOL_BRANCH) {
531         int temp,tright;
532         exp->_p++;
533         temp = trex_newnode(exp,OP_OR);
534         exp->_nodes[temp].left = ret;
535         tright = trex_list(exp);
536         exp->_nodes[temp].right = tright;
537         ret = temp;
538     }
539     return ret;
542 static TRexBool trex_matchcclass(int cclass,TRexChar c)
544     switch(cclass) {
545     case 'a': return isalpha(c)?TRex_True:TRex_False;
546     case 'A': return !isalpha(c)?TRex_True:TRex_False;
547     case 'w': return (isalnum(c) || c == '_')?TRex_True:TRex_False;
548     case 'W': return (!isalnum(c) && c != '_')?TRex_True:TRex_False;
549     case 's': return isspace(c)?TRex_True:TRex_False;
550     case 'S': return !isspace(c)?TRex_True:TRex_False;
551     case 'd': return isdigit(c)?TRex_True:TRex_False;
552     case 'D': return !isdigit(c)?TRex_True:TRex_False;
553     case 'x': return isxdigit(c)?TRex_True:TRex_False;
554     case 'X': return !isxdigit(c)?TRex_True:TRex_False;
555     case 'c': return iscntrl(c)?TRex_True:TRex_False;
556     case 'C': return !iscntrl(c)?TRex_True:TRex_False;
557     case 'p': return ispunct(c)?TRex_True:TRex_False;
558     case 'P': return !ispunct(c)?TRex_True:TRex_False;
559     case 'l': return islower(c)?TRex_True:TRex_False;
560     case 'u': return isupper(c)?TRex_True:TRex_False;
561     }
562     return TRex_False; /*cannot happen*/
565 static TRexBool trex_matchclass(TRex* exp,TRexNode *node,TRexChar c)
567     do {
568         switch(node->type) {
569             case OP_RANGE:
570                 if(c >= node->left && c <= node->right) return TRex_True;
571                 break;
572             case OP_CCLASS:
573                 if(trex_matchcclass(node->left,c)) return TRex_True;
574                 break;
575             default:
576                 if(c == node->type)return TRex_True;
577         }
578     } while((node->next != -1) && (node = &exp->_nodes[node->next]));
579     return TRex_False;
582 static const TRexChar *trex_matchnode(TRex* exp,TRexNode *node,const TRexChar *str,TRexNode *next)
584     
585     TRexNodeType type = node->type;
586     switch(type) {
587     case OP_GREEDY: {
588         //TRexNode *greedystop = (node->next != -1) ? &exp->_nodes[node->next] : NULL;
589         TRexNode *greedystop = NULL;
590         int p0 = (node->right >> 16)&0x0000FFFF, p1 = node->right&0x0000FFFF, nmaches = 0;
591         const TRexChar *s=str, *good = str;
593         if(node->next != -1) {
594             greedystop = &exp->_nodes[node->next];
595         }
596         else {
597             greedystop = next;
598         }
600         while((nmaches == 0xFFFF || nmaches < p1)) {
602             const TRexChar *stop;
603             if(!(s = trex_matchnode(exp,&exp->_nodes[node->left],s,greedystop)))
604                 break;
605             nmaches++;
606             good=s;
607             if(greedystop) {
608                 //checks that 0 matches satisfy the expression(if so skips)
609                 //if not would always stop(for instance if is a '?')
610                 if(greedystop->type != OP_GREEDY ||
611                 (greedystop->type == OP_GREEDY && ((greedystop->right >> 16)&0x0000FFFF) != 0))
612                 {
613                     TRexNode *gnext = NULL;
614                     if(greedystop->next != -1) {
615                         gnext = &exp->_nodes[greedystop->next];
616                     }else if(next && next->next != -1){
617                         gnext = &exp->_nodes[next->next];
618                     }
619                     stop = trex_matchnode(exp,greedystop,s,gnext);
620                     if(stop) {
621                         //if satisfied stop it
622                         if(p0 == p1 && p0 == nmaches) break;
623                         else if(nmaches >= p0 && p1 == 0xFFFF) break;
624                         else if(nmaches >= p0 && nmaches <= p1) break;
625                     }
626                 }
627             }
628             
629             if(s >= exp->_eol)
630                 break;
631         }
632         if(p0 == p1 && p0 == nmaches) return good;
633         else if(nmaches >= p0 && p1 == 0xFFFF) return good;
634         else if(nmaches >= p0 && nmaches <= p1) return good;
635         return NULL;
636     }
637     case OP_OR: {
638             const TRexChar *asd = str;
639             TRexNode *temp=&exp->_nodes[node->left];
640             while( (asd = trex_matchnode(exp,temp,asd,NULL)) ) {
641                 if(temp->next != -1)
642                     temp = &exp->_nodes[temp->next];
643                 else
644                     return asd;
645             }
646             asd = str;
647             temp = &exp->_nodes[node->right];
648             while( (asd = trex_matchnode(exp,temp,asd,NULL)) ) {
649                 if(temp->next != -1)
650                     temp = &exp->_nodes[temp->next];
651                 else
652                     return asd;
653             }
654             return NULL;
655             break;
656     }
657     case OP_EXPR:
658     case OP_NOCAPEXPR:{
659             TRexNode *n = &exp->_nodes[node->left];
660             const TRexChar *cur = str;
661             int capture = -1;
662             if(node->type != OP_NOCAPEXPR && node->right == exp->_currsubexp) {
663                 capture = exp->_currsubexp;
664                 exp->_matches[capture].begin = cur;
665                 exp->_currsubexp++;
666             }
667             
668             do {
669                 TRexNode *subnext = NULL;
670                 if(n->next != -1) {
671                     subnext = &exp->_nodes[n->next];
672                 }else {
673                     subnext = next;
674                 }
675                 if(!(cur = trex_matchnode(exp,n,cur,subnext))) {
676                     if(capture != -1){
677                         exp->_matches[capture].begin = 0;
678                         exp->_matches[capture].len = 0;
679                     }
680                     return NULL;
681                 }
682             } while((n->next != -1) && (n = &exp->_nodes[n->next]));
684             if(capture != -1) 
685                 exp->_matches[capture].len = cur - exp->_matches[capture].begin;
686             return cur;
687     }                 
688     case OP_WB:
689         if(str == exp->_bol && !isspace(*str)
690          || (str == exp->_eol && !isspace(*(str-1)))
691          || (!isspace(*str) && isspace(*(str+1)))
692          || (isspace(*str) && !isspace(*(str+1))) ) {
693             return (node->left == 'b')?str:NULL;
694         }
695         return (node->left == 'b')?NULL:str;
696     case OP_BOL:
697         if(str == exp->_bol) return str;
698         return NULL;
699     case OP_EOL:
700         if(str == exp->_eol) return str;
701         return NULL;
702     case OP_DOT:{
703         *str++;
704                 }
705         return str;
706     case OP_NCLASS:
707     case OP_CLASS:
708         if(trex_matchclass(exp,&exp->_nodes[node->left],*str)?(type == OP_CLASS?TRex_True:TRex_False):(type == OP_NCLASS?TRex_True:TRex_False)) {
709             *str++;
710             return str;
711         }
712         return NULL;
713     case OP_CCLASS:
714         if(trex_matchcclass(node->left,*str)) {
715             *str++;
716             return str;
717         }
718         return NULL;
719     default: /* char */
720         if(*str != node->type) return NULL;
721         *str++;
722         return str;
723     }
724     return NULL;
727 /* public api */
728 TRex *trex_compile(const TRexChar *pattern,const TRexChar **error)
730     TRex *exp = (TRex *)malloc(sizeof(TRex));
731     exp->_eol = exp->_bol = NULL;
732     exp->_p = pattern;
733     exp->_nallocated = (int)scstrlen(pattern) * sizeof(TRexChar);
734     exp->_nodes = (TRexNode *)malloc(exp->_nallocated * sizeof(TRexNode));
735     exp->_nsize = 0;
736     exp->_matches = 0;
737     exp->_nsubexpr = 0;
738     exp->_first = trex_newnode(exp,OP_EXPR);
739     exp->_error = error;
740     exp->_jmpbuf = malloc(sizeof(jmp_buf));
741     if(setjmp(*((jmp_buf*)exp->_jmpbuf)) == 0) {
742         int res = trex_list(exp);
743         exp->_nodes[exp->_first].left = res;
744         if(*exp->_p!='\0')
745             trex_error(exp,_SC("unexpected character"));
746 #ifdef _DEBUG
747         {
748             int nsize,i;
749             TRexNode *t;
750             nsize = exp->_nsize;
751             t = &exp->_nodes[0];
752             scprintf(_SC("\n"));
753             for(i = 0;i < nsize; i++) {
754                 if(exp->_nodes[i].type>MAX_CHAR)
755                     scprintf(_SC("[%02d] %10s "),i,g_nnames[exp->_nodes[i].type-MAX_CHAR]);
756                 else
757                     scprintf(_SC("[%02d] %10c "),i,exp->_nodes[i].type);
758                 scprintf(_SC("left %02d right %02d next %02d\n"),exp->_nodes[i].left,exp->_nodes[i].right,exp->_nodes[i].next);
759             }
760             scprintf(_SC("\n"));
761         }
762 #endif
763         exp->_matches = (TRexMatch *) malloc(exp->_nsubexpr * sizeof(TRexMatch));
764         memset(exp->_matches,0,exp->_nsubexpr * sizeof(TRexMatch));
765     }
766     else{
767         trex_free(exp);
768         return NULL;
769     }
770     return exp;
773 void trex_free(TRex *exp)
775     if(exp)    {
776         if(exp->_nodes) free(exp->_nodes);
777         if(exp->_jmpbuf) free(exp->_jmpbuf);
778         if(exp->_matches) free(exp->_matches);
779         free(exp);
780     }
783 TRexBool trex_match(TRex* exp,const TRexChar* text)
785     const TRexChar* res = NULL;
786     exp->_bol = text;
787     exp->_eol = text + scstrlen(text);
788     exp->_currsubexp = 0;
789     res = trex_matchnode(exp,exp->_nodes,text,NULL);
790     if(res == NULL || res != exp->_eol)
791         return TRex_False;
792     return TRex_True;
795 TRexBool trex_searchrange(TRex* exp,const TRexChar* text_begin,const TRexChar* text_end,const TRexChar** out_begin, const TRexChar** out_end)
797     const TRexChar *cur = NULL;
798     int node = exp->_first;
799     if(text_begin >= text_end) return TRex_False;
800     exp->_bol = text_begin;
801     exp->_eol = text_end;
802     do {
803         cur = text_begin;
804         while(node != -1) {
805             exp->_currsubexp = 0;
806             cur = trex_matchnode(exp,&exp->_nodes[node],cur,NULL);
807             if(!cur)
808                 break;
809             node = exp->_nodes[node].next;
810         }
811         *text_begin++;
812     } while(cur == NULL && text_begin != text_end);
814     if(cur == NULL)
815         return TRex_False;
817     --text_begin;
819     if(out_begin) *out_begin = text_begin;
820     if(out_end) *out_end = cur;
821     return TRex_True;
824 TRexBool trex_search(TRex* exp,const TRexChar* text, const TRexChar** out_begin, const TRexChar** out_end)
826     return trex_searchrange(exp,text,text + scstrlen(text),out_begin,out_end);
829 int trex_getsubexpcount(TRex* exp)
831     return exp->_nsubexpr;
834 TRexBool trex_getsubexp(TRex* exp, int n, TRexMatch *subexp)
836     if( n<0 || n >= exp->_nsubexpr) return TRex_False;
837     *subexp = exp->_matches[n];
838     return TRex_True;
842 //########################################################################
843 //########################################################################
844 //##  E N D    R E G E X P
845 //########################################################################
846 //########################################################################
852 //########################################################################
853 //########################################################################
854 //##  X M L
855 //########################################################################
856 //########################################################################
858 // Note:  This mini-dom library comes from Pedro, another little project
859 // of mine.
861 typedef std::string String;
862 typedef unsigned int XMLCh;
865 class Namespace
867 public:
868     Namespace()
869         {}
871     Namespace(const String &prefixArg, const String &namespaceURIArg)
872         {
873         prefix       = prefixArg;
874         namespaceURI = namespaceURIArg;
875         }
877     Namespace(const Namespace &other)
878         {
879         assign(other);
880         }
882     Namespace &operator=(const Namespace &other)
883         {
884         assign(other);
885         return *this;
886         }
888     virtual ~Namespace()
889         {}
891     virtual String getPrefix()
892         { return prefix; }
894     virtual String getNamespaceURI()
895         { return namespaceURI; }
897 protected:
899     void assign(const Namespace &other)
900         {
901         prefix       = other.prefix;
902         namespaceURI = other.namespaceURI;
903         }
905     String prefix;
906     String namespaceURI;
908 };
910 class Attribute
912 public:
913     Attribute()
914         {}
916     Attribute(const String &nameArg, const String &valueArg)
917         {
918         name  = nameArg;
919         value = valueArg;
920         }
922     Attribute(const Attribute &other)
923         {
924         assign(other);
925         }
927     Attribute &operator=(const Attribute &other)
928         {
929         assign(other);
930         return *this;
931         }
933     virtual ~Attribute()
934         {}
936     virtual String getName()
937         { return name; }
939     virtual String getValue()
940         { return value; }
942 protected:
944     void assign(const Attribute &other)
945         {
946         name  = other.name;
947         value = other.value;
948         }
950     String name;
951     String value;
953 };
956 class Element
958 friend class Parser;
960 public:
961     Element()
962         {
963         init();
964         }
966     Element(const String &nameArg)
967         {
968         init();
969         name   = nameArg;
970         }
972     Element(const String &nameArg, const String &valueArg)
973         {
974         init();
975         name   = nameArg;
976         value  = valueArg;
977         }
979     Element(const Element &other)
980         {
981         assign(other);
982         }
984     Element &operator=(const Element &other)
985         {
986         assign(other);
987         return *this;
988         }
990     virtual Element *clone();
992     virtual ~Element()
993         {
994         for (unsigned int i=0 ; i<children.size() ; i++)
995             delete children[i];
996         }
998     virtual String getName()
999         { return name; }
1001     virtual String getValue()
1002         { return value; }
1004     Element *getParent()
1005         { return parent; }
1007     std::vector<Element *> getChildren()
1008         { return children; }
1010     std::vector<Element *> findElements(const String &name);
1012     String getAttribute(const String &name);
1014     std::vector<Attribute> &getAttributes()
1015         { return attributes; } 
1017     String getTagAttribute(const String &tagName, const String &attrName);
1019     String getTagValue(const String &tagName);
1021     void addChild(Element *child);
1023     void addAttribute(const String &name, const String &value);
1025     void addNamespace(const String &prefix, const String &namespaceURI);
1028     /**
1029      * Prettyprint an XML tree to an output stream.  Elements are indented
1030      * according to element hierarchy.
1031      * @param f a stream to receive the output
1032      * @param elem the element to output
1033      */
1034     void writeIndented(FILE *f);
1036     /**
1037      * Prettyprint an XML tree to standard output.  This is the equivalent of
1038      * writeIndented(stdout).
1039      * @param elem the element to output
1040      */
1041     void print();
1042     
1043     int getLine()
1044         { return line; }
1046 protected:
1048     void init()
1049         {
1050         parent = NULL;
1051         line   = 0;
1052         }
1054     void assign(const Element &other)
1055         {
1056         parent     = other.parent;
1057         children   = other.children;
1058         attributes = other.attributes;
1059         namespaces = other.namespaces;
1060         name       = other.name;
1061         value      = other.value;
1062         line       = other.line;
1063         }
1065     void findElementsRecursive(std::vector<Element *>&res, const String &name);
1067     void writeIndentedRecursive(FILE *f, int indent);
1069     Element *parent;
1071     std::vector<Element *>children;
1073     std::vector<Attribute> attributes;
1074     std::vector<Namespace> namespaces;
1076     String name;
1077     String value;
1078     
1079     int line;
1080 };
1086 class Parser
1088 public:
1089     /**
1090      * Constructor
1091      */
1092     Parser()
1093         { init(); }
1095     virtual ~Parser()
1096         {}
1098     /**
1099      * Parse XML in a char buffer.
1100      * @param buf a character buffer to parse
1101      * @param pos position to start parsing
1102      * @param len number of chars, from pos, to parse.
1103      * @return a pointer to the root of the XML document;
1104      */
1105     Element *parse(const char *buf,int pos,int len);
1107     /**
1108      * Parse XML in a char buffer.
1109      * @param buf a character buffer to parse
1110      * @param pos position to start parsing
1111      * @param len number of chars, from pos, to parse.
1112      * @return a pointer to the root of the XML document;
1113      */
1114     Element *parse(const String &buf);
1116     /**
1117      * Parse a named XML file.  The file is loaded like a data file;
1118      * the original format is not preserved.
1119      * @param fileName the name of the file to read
1120      * @return a pointer to the root of the XML document;
1121      */
1122     Element *parseFile(const String &fileName);
1124     /**
1125      * Utility method to preprocess a string for XML
1126      * output, escaping its entities.
1127      * @param str the string to encode
1128      */
1129     static String encode(const String &str);
1131     /**
1132      *  Removes whitespace from beginning and end of a string
1133      */
1134     String trim(const String &s);
1136 private:
1138     void init()
1139         {
1140         keepGoing       = true;
1141         currentNode     = NULL;
1142         parselen        = 0;
1143         parsebuf        = NULL;
1144         currentPosition = 0;
1145         }
1147     int countLines(int begin, int end);
1149     void getLineAndColumn(int pos, int *lineNr, int *colNr);
1151     void error(char *fmt, ...);
1153     int peek(int pos);
1155     int match(int pos, const char *text);
1157     int skipwhite(int p);
1159     int getWord(int p0, String &buf);
1161     int getQuoted(int p0, String &buf, int do_i_parse);
1163     int parseVersion(int p0);
1165     int parseDoctype(int p0);
1167     int parseElement(int p0, Element *par,int depth);
1169     Element *parse(XMLCh *buf,int pos,int len);
1171     bool       keepGoing;
1172     Element    *currentNode;
1173     int        parselen;
1174     XMLCh      *parsebuf;
1175     String     cdatabuf;
1176     int        currentPosition;
1177 };
1182 //########################################################################
1183 //# E L E M E N T
1184 //########################################################################
1186 Element *Element::clone()
1188     Element *elem = new Element(name, value);
1189     elem->parent     = parent;
1190     elem->attributes = attributes;
1191     elem->namespaces = namespaces;
1192     elem->line       = line;
1194     std::vector<Element *>::iterator iter;
1195     for (iter = children.begin(); iter != children.end() ; iter++)
1196         {
1197         elem->addChild((*iter)->clone());
1198         }
1199     return elem;
1203 void Element::findElementsRecursive(std::vector<Element *>&res, const String &name)
1205     if (getName() == name)
1206         {
1207         res.push_back(this);
1208         }
1209     for (unsigned int i=0; i<children.size() ; i++)
1210         children[i]->findElementsRecursive(res, name);
1213 std::vector<Element *> Element::findElements(const String &name)
1215     std::vector<Element *> res;
1216     findElementsRecursive(res, name);
1217     return res;
1220 String Element::getAttribute(const String &name)
1222     for (unsigned int i=0 ; i<attributes.size() ; i++)
1223         if (attributes[i].getName() ==name)
1224             return attributes[i].getValue();
1225     return "";
1228 String Element::getTagAttribute(const String &tagName, const String &attrName)
1230     std::vector<Element *>elems = findElements(tagName);
1231     if (elems.size() <1)
1232         return "";
1233     String res = elems[0]->getAttribute(attrName);
1234     return res;
1237 String Element::getTagValue(const String &tagName)
1239     std::vector<Element *>elems = findElements(tagName);
1240     if (elems.size() <1)
1241         return "";
1242     String res = elems[0]->getValue();
1243     return res;
1246 void Element::addChild(Element *child)
1248     if (!child)
1249         return;
1250     child->parent = this;
1251     children.push_back(child);
1255 void Element::addAttribute(const String &name, const String &value)
1257     Attribute attr(name, value);
1258     attributes.push_back(attr);
1261 void Element::addNamespace(const String &prefix, const String &namespaceURI)
1263     Namespace ns(prefix, namespaceURI);
1264     namespaces.push_back(ns);
1267 void Element::writeIndentedRecursive(FILE *f, int indent)
1269     int i;
1270     if (!f)
1271         return;
1272     //Opening tag, and attributes
1273     for (i=0;i<indent;i++)
1274         fputc(' ',f);
1275     fprintf(f,"<%s",name.c_str());
1276     for (unsigned int i=0 ; i<attributes.size() ; i++)
1277         {
1278         fprintf(f," %s=\"%s\"",
1279               attributes[i].getName().c_str(),
1280               attributes[i].getValue().c_str());
1281         }
1282     for (unsigned int i=0 ; i<namespaces.size() ; i++)
1283         {
1284         fprintf(f," xmlns:%s=\"%s\"",
1285               namespaces[i].getPrefix().c_str(),
1286               namespaces[i].getNamespaceURI().c_str());
1287         }
1288     fprintf(f,">\n");
1290     //Between the tags
1291     if (value.size() > 0)
1292         {
1293         for (int i=0;i<indent;i++)
1294             fputc(' ', f);
1295         fprintf(f," %s\n", value.c_str());
1296         }
1298     for (unsigned int i=0 ; i<children.size() ; i++)
1299         children[i]->writeIndentedRecursive(f, indent+2);
1301     //Closing tag
1302     for (int i=0; i<indent; i++)
1303         fputc(' ',f);
1304     fprintf(f,"</%s>\n", name.c_str());
1307 void Element::writeIndented(FILE *f)
1309     writeIndentedRecursive(f, 0);
1312 void Element::print()
1314     writeIndented(stdout);
1318 //########################################################################
1319 //# P A R S E R
1320 //########################################################################
1324 typedef struct
1325     {
1326     char *escaped;
1327     char value;
1328     } EntityEntry;
1330 static EntityEntry entities[] =
1332     { "&amp;" , '&'  },
1333     { "&lt;"  , '<'  },
1334     { "&gt;"  , '>'  },
1335     { "&apos;", '\'' },
1336     { "&quot;", '"'  },
1337     { NULL    , '\0' }
1338 };
1342 /**
1343  *  Removes whitespace from beginning and end of a string
1344  */
1345 String Parser::trim(const String &s)
1347     if (s.size() < 1)
1348         return s;
1349     
1350     //Find first non-ws char
1351     unsigned int begin = 0;
1352     for ( ; begin < s.size() ; begin++)
1353         {
1354         if (!isspace(s[begin]))
1355             break;
1356         }
1358     //Find first non-ws char, going in reverse
1359     unsigned int end = s.size() - 1;
1360     for ( ; end > begin ; end--)
1361         {
1362         if (!isspace(s[end]))
1363             break;
1364         }
1365     //trace("begin:%d  end:%d", begin, end);
1367     String res = s.substr(begin, end-begin+1);
1368     return res;
1372 int Parser::countLines(int begin, int end)
1374     int count = 0;
1375     for (int i=begin ; i<end ; i++)
1376         {
1377         XMLCh ch = parsebuf[i];
1378         if (ch == '\n' || ch == '\r')
1379             count++;
1380         }
1381     return count;
1385 void Parser::getLineAndColumn(int pos, int *lineNr, int *colNr)
1387     int line = 1;
1388     int col  = 1;
1389     for (long i=0 ; i<pos ; i++)
1390         {
1391         XMLCh ch = parsebuf[i];
1392         if (ch == '\n' || ch == '\r')
1393             {
1394             col = 0;
1395             line ++;
1396             }
1397         else
1398             col++;
1399         }
1400     *lineNr = line;
1401     *colNr  = col;
1406 void Parser::error(char *fmt, ...)
1408     int lineNr;
1409     int colNr;
1410     getLineAndColumn(currentPosition, &lineNr, &colNr);
1411     va_list args;
1412     fprintf(stderr, "xml error at line %d, column %d:", lineNr, colNr);
1413     va_start(args,fmt);
1414     vfprintf(stderr,fmt,args);
1415     va_end(args) ;
1416     fprintf(stderr, "\n");
1421 int Parser::peek(int pos)
1423     if (pos >= parselen)
1424         return -1;
1425     currentPosition = pos;
1426     int ch = parsebuf[pos];
1427     //printf("ch:%c\n", ch);
1428     return ch;
1433 String Parser::encode(const String &str)
1435     String ret;
1436     for (unsigned int i=0 ; i<str.size() ; i++)
1437         {
1438         XMLCh ch = (XMLCh)str[i];
1439         if (ch == '&')
1440             ret.append("&amp;");
1441         else if (ch == '<')
1442             ret.append("&lt;");
1443         else if (ch == '>')
1444             ret.append("&gt;");
1445         else if (ch == '\'')
1446             ret.append("&apos;");
1447         else if (ch == '"')
1448             ret.append("&quot;");
1449         else
1450             ret.push_back(ch);
1452         }
1453     return ret;
1457 int Parser::match(int p0, const char *text)
1459     int p = p0;
1460     while (*text)
1461         {
1462         if (peek(p) != *text)
1463             return p0;
1464         p++; text++;
1465         }
1466     return p;
1471 int Parser::skipwhite(int p)
1474     while (p<parselen)
1475         {
1476         int p2 = match(p, "<!--");
1477         if (p2 > p)
1478             {
1479             p = p2;
1480             while (p<parselen)
1481               {
1482               p2 = match(p, "-->");
1483               if (p2 > p)
1484                   {
1485                   p = p2;
1486                   break;
1487                   }
1488               p++;
1489               }
1490           }
1491       XMLCh b = peek(p);
1492       if (!isspace(b))
1493           break;
1494       p++;
1495       }
1496   return p;
1499 /* modify this to allow all chars for an element or attribute name*/
1500 int Parser::getWord(int p0, String &buf)
1502     int p = p0;
1503     while (p<parselen)
1504         {
1505         XMLCh b = peek(p);
1506         if (b<=' ' || b=='/' || b=='>' || b=='=')
1507             break;
1508         buf.push_back(b);
1509         p++;
1510         }
1511     return p;
1514 int Parser::getQuoted(int p0, String &buf, int do_i_parse)
1517     int p = p0;
1518     if (peek(p) != '"' && peek(p) != '\'')
1519         return p0;
1520     p++;
1522     while ( p<parselen )
1523         {
1524         XMLCh b = peek(p);
1525         if (b=='"' || b=='\'')
1526             break;
1527         if (b=='&' && do_i_parse)
1528             {
1529             bool found = false;
1530             for (EntityEntry *ee = entities ; ee->value ; ee++)
1531                 {
1532                 int p2 = match(p, ee->escaped);
1533                 if (p2>p)
1534                     {
1535                     buf.push_back(ee->value);
1536                     p = p2;
1537                     found = true;
1538                     break;
1539                     }
1540                 }
1541             if (!found)
1542                 {
1543                 error("unterminated entity");
1544                 return false;
1545                 }
1546             }
1547         else
1548             {
1549             buf.push_back(b);
1550             p++;
1551             }
1552         }
1553     return p;
1556 int Parser::parseVersion(int p0)
1558     //printf("### parseVersion: %d\n", p0);
1560     int p = p0;
1562     p = skipwhite(p0);
1564     if (peek(p) != '<')
1565         return p0;
1567     p++;
1568     if (p>=parselen || peek(p)!='?')
1569         return p0;
1571     p++;
1573     String buf;
1575     while (p<parselen)
1576         {
1577         XMLCh ch = peek(p);
1578         if (ch=='?')
1579             {
1580             p++;
1581             break;
1582             }
1583         buf.push_back(ch);
1584         p++;
1585         }
1587     if (peek(p) != '>')
1588         return p0;
1589     p++;
1591     //printf("Got version:%s\n",buf.c_str());
1592     return p;
1595 int Parser::parseDoctype(int p0)
1597     //printf("### parseDoctype: %d\n", p0);
1599     int p = p0;
1600     p = skipwhite(p);
1602     if (p>=parselen || peek(p)!='<')
1603         return p0;
1605     p++;
1607     if (peek(p)!='!' || peek(p+1)=='-')
1608         return p0;
1609     p++;
1611     String buf;
1612     while (p<parselen)
1613         {
1614         XMLCh ch = peek(p);
1615         if (ch=='>')
1616             {
1617             p++;
1618             break;
1619             }
1620         buf.push_back(ch);
1621         p++;
1622         }
1624     //printf("Got doctype:%s\n",buf.c_str());
1625     return p;
1630 int Parser::parseElement(int p0, Element *par,int lineNr)
1633     int p = p0;
1635     int p2 = p;
1637     p = skipwhite(p);
1639     //## Get open tag
1640     XMLCh ch = peek(p);
1641     if (ch!='<')
1642         return p0;
1644     int line, col;
1645     //getLineAndColumn(p, &line, &col);
1647     p++;
1649     String openTagName;
1650     p = skipwhite(p);
1651     p = getWord(p, openTagName);
1652     //printf("####tag :%s\n", openTagName.c_str());
1653     p = skipwhite(p);
1655     //Add element to tree
1656     Element *n = new Element(openTagName);
1657     n->line = lineNr + countLines(p0, p);
1658     n->parent = par;
1659     par->addChild(n);
1661     // Get attributes
1662     if (peek(p) != '>')
1663         {
1664         while (p<parselen)
1665             {
1666             p = skipwhite(p);
1667             ch = peek(p);
1668             //printf("ch:%c\n",ch);
1669             if (ch=='>')
1670                 break;
1671             else if (ch=='/' && p<parselen+1)
1672                 {
1673                 p++;
1674                 p = skipwhite(p);
1675                 ch = peek(p);
1676                 if (ch=='>')
1677                     {
1678                     p++;
1679                     //printf("quick close\n");
1680                     return p;
1681                     }
1682                 }
1683             String attrName;
1684             p2 = getWord(p, attrName);
1685             if (p2==p)
1686                 break;
1687             //printf("name:%s",buf);
1688             p=p2;
1689             p = skipwhite(p);
1690             ch = peek(p);
1691             //printf("ch:%c\n",ch);
1692             if (ch!='=')
1693                 break;
1694             p++;
1695             p = skipwhite(p);
1696             // ch = parsebuf[p];
1697             // printf("ch:%c\n",ch);
1698             String attrVal;
1699             p2 = getQuoted(p, attrVal, true);
1700             p=p2+1;
1701             //printf("name:'%s'   value:'%s'\n",attrName.c_str(),attrVal.c_str());
1702             char *namestr = (char *)attrName.c_str();
1703             if (strncmp(namestr, "xmlns:", 6)==0)
1704                 n->addNamespace(attrName, attrVal);
1705             else
1706                 n->addAttribute(attrName, attrVal);
1707             }
1708         }
1710     bool cdata = false;
1712     p++;
1713     // ### Get intervening data ### */
1714     String data;
1715     while (p<parselen)
1716         {
1717         //# COMMENT
1718         p2 = match(p, "<!--");
1719         if (!cdata && p2>p)
1720             {
1721             p = p2;
1722             while (p<parselen)
1723                 {
1724                 p2 = match(p, "-->");
1725                 if (p2 > p)
1726                     {
1727                     p = p2;
1728                     break;
1729                     }
1730                 p++;
1731                 }
1732             }
1734         ch = peek(p);
1735         //# END TAG
1736         if (ch=='<' && !cdata && peek(p+1)=='/')
1737             {
1738             break;
1739             }
1740         //# CDATA
1741         p2 = match(p, "<![CDATA[");
1742         if (p2 > p)
1743             {
1744             cdata = true;
1745             p = p2;
1746             continue;
1747             }
1749         //# CHILD ELEMENT
1750         if (ch == '<')
1751             {
1752             p2 = parseElement(p, n, lineNr + countLines(p0, p));
1753             if (p2 == p)
1754                 {
1755                 /*
1756                 printf("problem on element:%s.  p2:%d p:%d\n",
1757                       openTagName.c_str(), p2, p);
1758                 */
1759                 return p0;
1760                 }
1761             p = p2;
1762             continue;
1763             }
1764         //# ENTITY
1765         if (ch=='&' && !cdata)
1766             {
1767             bool found = false;
1768             for (EntityEntry *ee = entities ; ee->value ; ee++)
1769                 {
1770                 int p2 = match(p, ee->escaped);
1771                 if (p2>p)
1772                     {
1773                     data.push_back(ee->value);
1774                     p = p2;
1775                     found = true;
1776                     break;
1777                     }
1778                 }
1779             if (!found)
1780                 {
1781                 error("unterminated entity");
1782                 return -1;
1783                 }
1784             continue;
1785             }
1787         //# NONE OF THE ABOVE
1788         data.push_back(ch);
1789         p++;
1790         }/*while*/
1793     n->value = data;
1794     //printf("%d : data:%s\n",p,data.c_str());
1796     //## Get close tag
1797     p = skipwhite(p);
1798     ch = peek(p);
1799     if (ch != '<')
1800         {
1801         error("no < for end tag\n");
1802         return p0;
1803         }
1804     p++;
1805     ch = peek(p);
1806     if (ch != '/')
1807         {
1808         error("no / on end tag");
1809         return p0;
1810         }
1811     p++;
1812     ch = peek(p);
1813     p = skipwhite(p);
1814     String closeTagName;
1815     p = getWord(p, closeTagName);
1816     if (openTagName != closeTagName)
1817         {
1818         error("Mismatched closing tag.  Expected </%S>. Got '%S'.",
1819                 openTagName.c_str(), closeTagName.c_str());
1820         return p0;
1821         }
1822     p = skipwhite(p);
1823     if (peek(p) != '>')
1824         {
1825         error("no > on end tag for '%s'", closeTagName.c_str());
1826         return p0;
1827         }
1828     p++;
1829     // printf("close element:%s\n",closeTagName.c_str());
1830     p = skipwhite(p);
1831     return p;
1837 Element *Parser::parse(XMLCh *buf,int pos,int len)
1839     parselen = len;
1840     parsebuf = buf;
1841     Element *rootNode = new Element("root");
1842     pos = parseVersion(pos);
1843     pos = parseDoctype(pos);
1844     pos = parseElement(pos, rootNode, 1);
1845     return rootNode;
1849 Element *Parser::parse(const char *buf, int pos, int len)
1851     XMLCh *charbuf = new XMLCh[len + 1];
1852     long i = 0;
1853     for ( ; i < len ; i++)
1854         charbuf[i] = (XMLCh)buf[i];
1855     charbuf[i] = '\0';
1857     Element *n = parse(charbuf, pos, len);
1858     delete[] charbuf;
1859     return n;
1862 Element *Parser::parse(const String &buf)
1864     long len = (long)buf.size();
1865     XMLCh *charbuf = new XMLCh[len + 1];
1866     long i = 0;
1867     for ( ; i < len ; i++)
1868         charbuf[i] = (XMLCh)buf[i];
1869     charbuf[i] = '\0';
1871     Element *n = parse(charbuf, 0, len);
1872     delete[] charbuf;
1873     return n;
1876 Element *Parser::parseFile(const String &fileName)
1879     //##### LOAD INTO A CHAR BUF, THEN CONVERT TO XMLCh
1880     FILE *f = fopen(fileName.c_str(), "rb");
1881     if (!f)
1882         return NULL;
1884     struct stat  statBuf;
1885     if (fstat(fileno(f),&statBuf)<0)
1886         {
1887         fclose(f);
1888         return NULL;
1889         }
1890     long filelen = statBuf.st_size;
1892     //printf("length:%d\n",filelen);
1893     XMLCh *charbuf = new XMLCh[filelen + 1];
1894     for (XMLCh *p=charbuf ; !feof(f) ; p++)
1895         {
1896         *p = (XMLCh)fgetc(f);
1897         }
1898     fclose(f);
1899     charbuf[filelen] = '\0';
1902     /*
1903     printf("nrbytes:%d\n",wc_count);
1904     printf("buf:%ls\n======\n",charbuf);
1905     */
1906     Element *n = parse(charbuf, 0, filelen);
1907     delete[] charbuf;
1908     return n;
1911 //########################################################################
1912 //########################################################################
1913 //##  E N D    X M L
1914 //########################################################################
1915 //########################################################################
1922 //########################################################################
1923 //########################################################################
1924 //##  U R I
1925 //########################################################################
1926 //########################################################################
1928 //This would normally be a call to a UNICODE function
1929 #define isLetter(x) isalpha(x)
1931 /**
1932  *  A class that implements the W3C URI resource reference.
1933  */
1934 class URI
1936 public:
1938     typedef enum
1939         {
1940         SCHEME_NONE =0,
1941         SCHEME_DATA,
1942         SCHEME_HTTP,
1943         SCHEME_HTTPS,
1944         SCHEME_FTP,
1945         SCHEME_FILE,
1946         SCHEME_LDAP,
1947         SCHEME_MAILTO,
1948         SCHEME_NEWS,
1949         SCHEME_TELNET
1950         } SchemeTypes;
1952     /**
1953      *
1954      */
1955     URI()
1956         {
1957         init();
1958         }
1960     /**
1961      *
1962      */
1963     URI(const String &str)
1964         {
1965         init();
1966         parse(str);
1967         }
1970     /**
1971      *
1972      */
1973     URI(const char *str)
1974         {
1975         init();
1976         String domStr = str;
1977         parse(domStr);
1978         }
1981     /**
1982      *
1983      */
1984     URI(const URI &other)
1985         {
1986         init();
1987         assign(other);
1988         }
1991     /**
1992      *
1993      */
1994     URI &operator=(const URI &other)
1995         {
1996         init();
1997         assign(other);
1998         return *this;
1999         }
2002     /**
2003      *
2004      */
2005     virtual ~URI()
2006         {}
2010     /**
2011      *
2012      */
2013     virtual bool parse(const String &str);
2015     /**
2016      *
2017      */
2018     virtual String toString() const;
2020     /**
2021      *
2022      */
2023     virtual int getScheme() const;
2025     /**
2026      *
2027      */
2028     virtual String getSchemeStr() const;
2030     /**
2031      *
2032      */
2033     virtual String getAuthority() const;
2035     /**
2036      *  Same as getAuthority, but if the port has been specified
2037      *  as host:port , the port will not be included
2038      */
2039     virtual String getHost() const;
2041     /**
2042      *
2043      */
2044     virtual int getPort() const;
2046     /**
2047      *
2048      */
2049     virtual String getPath() const;
2051     /**
2052      *
2053      */
2054     virtual String getNativePath() const;
2056     /**
2057      *
2058      */
2059     virtual bool isAbsolute() const;
2061     /**
2062      *
2063      */
2064     virtual bool isOpaque() const;
2066     /**
2067      *
2068      */
2069     virtual String getQuery() const;
2071     /**
2072      *
2073      */
2074     virtual String getFragment() const;
2076     /**
2077      *
2078      */
2079     virtual URI resolve(const URI &other) const;
2081     /**
2082      *
2083      */
2084     virtual void normalize();
2086 private:
2088     /**
2089      *
2090      */
2091     void init()
2092         {
2093         parsebuf  = NULL;
2094         parselen  = 0;
2095         scheme    = SCHEME_NONE;
2096         schemeStr = "";
2097         port      = 0;
2098         authority = "";
2099         path      = "";
2100         absolute  = false;
2101         opaque    = false;
2102         query     = "";
2103         fragment  = "";
2104         }
2107     /**
2108      *
2109      */
2110     void assign(const URI &other)
2111         {
2112         scheme    = other.scheme;
2113         schemeStr = other.schemeStr;
2114         authority = other.authority;
2115         port      = other.port;
2116         path      = other.path;
2117         absolute  = other.absolute;
2118         opaque    = other.opaque;
2119         query     = other.query;
2120         fragment  = other.fragment;
2121         }
2123     int scheme;
2125     String schemeStr;
2127     String authority;
2129     bool portSpecified;
2131     int port;
2133     String path;
2135     bool absolute;
2137     bool opaque;
2139     String query;
2141     String fragment;
2143     void error(const char *fmt, ...);
2145     void trace(const char *fmt, ...);
2148     int peek(int p);
2150     int match(int p, char *key);
2152     int parseScheme(int p);
2154     int parseHierarchicalPart(int p0);
2156     int parseQuery(int p0);
2158     int parseFragment(int p0);
2160     int parse(int p);
2162     char *parsebuf;
2164     int parselen;
2166 };
2170 typedef struct
2172     int  ival;
2173     char *sval;
2174     int  port;
2175 } LookupEntry;
2177 LookupEntry schemes[] =
2179     { URI::SCHEME_DATA,   "data:",    0 },
2180     { URI::SCHEME_HTTP,   "http:",   80 },
2181     { URI::SCHEME_HTTPS,  "https:", 443 },
2182     { URI::SCHEME_FTP,    "ftp",     12 },
2183     { URI::SCHEME_FILE,   "file:",    0 },
2184     { URI::SCHEME_LDAP,   "ldap:",  123 },
2185     { URI::SCHEME_MAILTO, "mailto:", 25 },
2186     { URI::SCHEME_NEWS,   "news:",  117 },
2187     { URI::SCHEME_TELNET, "telnet:", 23 },
2188     { 0,                  NULL,       0 }
2189 };
2192 String URI::toString() const
2194     String str = schemeStr;
2195     if (authority.size() > 0)
2196         {
2197         str.append("//");
2198         str.append(authority);
2199         }
2200     str.append(path);
2201     if (query.size() > 0)
2202         {
2203         str.append("?");
2204         str.append(query);
2205         }
2206     if (fragment.size() > 0)
2207         {
2208         str.append("#");
2209         str.append(fragment);
2210         }
2211     return str;
2215 int URI::getScheme() const
2217     return scheme;
2220 String URI::getSchemeStr() const
2222     return schemeStr;
2226 String URI::getAuthority() const
2228     String ret = authority;
2229     if (portSpecified && port>=0)
2230         {
2231         char buf[7];
2232         snprintf(buf, 6, ":%6d", port);
2233         ret.append(buf);
2234         }
2235     return ret;
2238 String URI::getHost() const
2240     return authority;
2243 int URI::getPort() const
2245     return port;
2249 String URI::getPath() const
2251     return path;
2254 String URI::getNativePath() const
2256     String npath;
2257 #ifdef __WIN32__
2258     unsigned int firstChar = 0;
2259     if (path.size() >= 3)
2260         {
2261         if (path[0] == '/' &&
2262             isLetter(path[1]) &&
2263             path[2] == ':')
2264             firstChar++;
2265          }
2266     for (unsigned int i=firstChar ; i<path.size() ; i++)
2267         {
2268         XMLCh ch = (XMLCh) path[i];
2269         if (ch == '/')
2270             npath.push_back((XMLCh)'\\');
2271         else
2272             npath.push_back(ch);
2273         }
2274 #else
2275     npath = path;
2276 #endif
2277     return npath;
2281 bool URI::isAbsolute() const
2283     return absolute;
2286 bool URI::isOpaque() const
2288     return opaque;
2292 String URI::getQuery() const
2294     return query;
2298 String URI::getFragment() const
2300     return fragment;
2304 URI URI::resolve(const URI &other) const
2306     //### According to w3c, this is handled in 3 cases
2308     //## 1
2309     if (opaque || other.isAbsolute())
2310         return other;
2312     //## 2
2313     if (other.fragment.size()  >  0 &&
2314         other.path.size()      == 0 &&
2315         other.scheme           == SCHEME_NONE &&
2316         other.authority.size() == 0 &&
2317         other.query.size()     == 0 )
2318         {
2319         URI fragUri = *this;
2320         fragUri.fragment = other.fragment;
2321         return fragUri;
2322         }
2324     //## 3 http://www.ietf.org/rfc/rfc2396.txt, section 5.2
2325     URI newUri;
2326     //# 3.1
2327     newUri.scheme    = scheme;
2328     newUri.schemeStr = schemeStr;
2329     newUri.query     = other.query;
2330     newUri.fragment  = other.fragment;
2331     if (other.authority.size() > 0)
2332         {
2333         //# 3.2
2334         if (absolute || other.absolute)
2335             newUri.absolute = true;
2336         newUri.authority = other.authority;
2337         newUri.port      = other.port;//part of authority
2338         newUri.path      = other.path;
2339         }
2340     else
2341         {
2342         //# 3.3
2343         if (other.absolute)
2344             {
2345             newUri.absolute = true;
2346             newUri.path     = other.path;
2347             }
2348         else
2349             {
2350             unsigned int pos = path.find_last_of('/');
2351             if (pos != path.npos)
2352                 {
2353                 String tpath = path.substr(0, pos+1);
2354                 tpath.append(other.path);
2355                 newUri.path = tpath;
2356                 }
2357             else
2358                 newUri.path = other.path;
2359             }
2360         }
2362     newUri.normalize();
2363     return newUri;
2368 /**
2369  *  This follows the Java URI algorithm:
2370  *   1. All "." segments are removed.
2371  *   2. If a ".." segment is preceded by a non-".." segment
2372  *          then both of these segments are removed. This step
2373  *          is repeated until it is no longer applicable.
2374  *   3. If the path is relative, and if its first segment
2375  *          contains a colon character (':'), then a "." segment
2376  *          is prepended. This prevents a relative URI with a path
2377  *          such as "a:b/c/d" from later being re-parsed as an
2378  *          opaque URI with a scheme of "a" and a scheme-specific
2379  *          part of "b/c/d". (Deviation from RFC 2396)
2380  */
2381 void URI::normalize()
2383     std::vector<String> segments;
2385     //## Collect segments
2386     if (path.size()<2)
2387         return;
2388     bool abs = false;
2389     unsigned int pos=0;
2390     if (path[0]=='/')
2391         {
2392         abs = true;
2393         pos++;
2394         }
2395     while (pos < path.size())
2396         {
2397         unsigned int pos2 = path.find('/', pos);
2398         if (pos2==path.npos)
2399             {
2400             String seg = path.substr(pos);
2401             //printf("last segment:%s\n", seg.c_str());
2402             segments.push_back(seg);
2403             break;
2404             }
2405         if (pos2>pos)
2406             {
2407             String seg = path.substr(pos, pos2-pos);
2408             //printf("segment:%s\n", seg.c_str());
2409             segments.push_back(seg);
2410             }
2411         pos = pos2;
2412         pos++;
2413         }
2415     //## Clean up (normalize) segments
2416     bool edited = false;
2417     std::vector<String>::iterator iter;
2418     for (iter=segments.begin() ; iter!=segments.end() ; )
2419         {
2420         String s = *iter;
2421         if (s == ".")
2422             {
2423             iter = segments.erase(iter);
2424             edited = true;
2425             }
2426         else if (s == ".." &&
2427                  iter != segments.begin() &&
2428                  *(iter-1) != "..")
2429             {
2430             iter--; //back up, then erase two entries
2431             iter = segments.erase(iter);
2432             iter = segments.erase(iter);
2433             edited = true;
2434             }
2435         else
2436             iter++;
2437         }
2439     //## Rebuild path, if necessary
2440     if (edited)
2441         {
2442         path.clear();
2443         if (abs)
2444             {
2445             path.append("/");
2446             }
2447         std::vector<String>::iterator iter;
2448         for (iter=segments.begin() ; iter!=segments.end() ; iter++)
2449             {
2450             if (iter != segments.begin())
2451                 path.append("/");
2452             path.append(*iter);
2453             }
2454         }
2460 //#########################################################################
2461 //# M E S S A G E S
2462 //#########################################################################
2464 void URI::error(const char *fmt, ...)
2466     va_list args;
2467     fprintf(stderr, "URI error: ");
2468     va_start(args, fmt);
2469     vfprintf(stderr, fmt, args);
2470     va_end(args);
2471     fprintf(stderr, "\n");
2474 void URI::trace(const char *fmt, ...)
2476     va_list args;
2477     fprintf(stdout, "URI: ");
2478     va_start(args, fmt);
2479     vfprintf(stdout, fmt, args);
2480     va_end(args);
2481     fprintf(stdout, "\n");
2487 //#########################################################################
2488 //# P A R S I N G
2489 //#########################################################################
2493 int URI::peek(int p)
2495     if (p<0 || p>=parselen)
2496         return -1;
2497     return parsebuf[p];
2502 int URI::match(int p0, char *key)
2504     int p = p0;
2505     while (p < parselen)
2506         {
2507         if (*key == '\0')
2508             return p;
2509         else if (*key != parsebuf[p])
2510             break;
2511         p++; key++;
2512         }
2513     return p0;
2516 //#########################################################################
2517 //#  Parsing is performed according to:
2518 //#  http://www.gbiv.com/protocols/uri/rfc/rfc3986.html#components
2519 //#########################################################################
2521 int URI::parseScheme(int p0)
2523     int p = p0;
2524     for (LookupEntry *entry = schemes; entry->sval ; entry++)
2525         {
2526         int p2 = match(p, entry->sval);
2527         if (p2 > p)
2528             {
2529             schemeStr = entry->sval;
2530             scheme    = entry->ival;
2531             port      = entry->port;
2532             p = p2;
2533             return p;
2534             }
2535         }
2537     return p;
2541 int URI::parseHierarchicalPart(int p0)
2543     int p = p0;
2544     int ch;
2546     //# Authority field (host and port, for example)
2547     int p2 = match(p, "//");
2548     if (p2 > p)
2549         {
2550         p = p2;
2551         portSpecified = false;
2552         String portStr;
2553         while (p < parselen)
2554             {
2555             ch = peek(p);
2556             if (ch == '/')
2557                 break;
2558             else if (ch == ':')
2559                 portSpecified = true;
2560             else if (portSpecified)
2561                 portStr.push_back((XMLCh)ch);
2562             else
2563                 authority.push_back((XMLCh)ch);
2564             p++;
2565             }
2566         if (portStr.size() > 0)
2567             {
2568             char *pstr = (char *)portStr.c_str();
2569             char *endStr;
2570             long val = strtol(pstr, &endStr, 10);
2571             if (endStr > pstr) //successful parse?
2572                 port = val;
2573             }
2574         }
2576     //# Are we absolute?
2577     ch = peek(p);
2578     if (isLetter(ch) && peek(p+1)==':')
2579         {
2580         absolute = true;
2581         path.push_back((XMLCh)'/');
2582         }
2583     else if (ch == '/')
2584         {
2585         absolute = true;
2586         if (p>p0) //in other words, if '/' is not the first char
2587             opaque = true;
2588         path.push_back((XMLCh)ch);
2589         p++;
2590         }
2592     while (p < parselen)
2593         {
2594         ch = peek(p);
2595         if (ch == '?' || ch == '#')
2596             break;
2597         path.push_back((XMLCh)ch);
2598         p++;
2599         }
2601     return p;
2604 int URI::parseQuery(int p0)
2606     int p = p0;
2607     int ch = peek(p);
2608     if (ch != '?')
2609         return p0;
2611     p++;
2612     while (p < parselen)
2613         {
2614         ch = peek(p);
2615         if (ch == '#')
2616             break;
2617         query.push_back((XMLCh)ch);
2618         p++;
2619         }
2622     return p;
2625 int URI::parseFragment(int p0)
2628     int p = p0;
2629     int ch = peek(p);
2630     if (ch != '#')
2631         return p0;
2633     p++;
2634     while (p < parselen)
2635         {
2636         ch = peek(p);
2637         if (ch == '?')
2638             break;
2639         fragment.push_back((XMLCh)ch);
2640         p++;
2641         }
2644     return p;
2648 int URI::parse(int p0)
2651     int p = p0;
2653     int p2 = parseScheme(p);
2654     if (p2 < 0)
2655         {
2656         error("Scheme");
2657         return -1;
2658         }
2659     p = p2;
2662     p2 = parseHierarchicalPart(p);
2663     if (p2 < 0)
2664         {
2665         error("Hierarchical part");
2666         return -1;
2667         }
2668     p = p2;
2670     p2 = parseQuery(p);
2671     if (p2 < 0)
2672         {
2673         error("Query");
2674         return -1;
2675         }
2676     p = p2;
2679     p2 = parseFragment(p);
2680     if (p2 < 0)
2681         {
2682         error("Fragment");
2683         return -1;
2684         }
2685     p = p2;
2687     return p;
2693 bool URI::parse(const String &str)
2695     init();
2696     
2697     parselen = str.size();
2699     String tmp;
2700     for (unsigned int i=0 ; i<str.size() ; i++)
2701         {
2702         XMLCh ch = (XMLCh) str[i];
2703         if (ch == '\\')
2704             tmp.push_back((XMLCh)'/');
2705         else
2706             tmp.push_back(ch);
2707         }
2708     parsebuf = (char *) tmp.c_str();
2711     int p = parse(0);
2712     normalize();
2714     if (p < 0)
2715         {
2716         error("Syntax error");
2717         return false;
2718         }
2720     //printf("uri:%s\n", toString().c_str());
2721     //printf("path:%s\n", path.c_str());
2723     return true;
2734 //########################################################################
2735 //########################################################################
2736 //##  M A K E
2737 //########################################################################
2738 //########################################################################
2740 //########################################################################
2741 //# F I L E S E T
2742 //########################################################################
2743 /**
2744  * This is the descriptor for a <fileset> item
2745  */
2746 class FileSet
2748 public:
2750     /**
2751      *
2752      */
2753     FileSet()
2754         {}
2756     /**
2757      *
2758      */
2759     FileSet(const FileSet &other)
2760         { assign(other); }
2762     /**
2763      *
2764      */
2765     FileSet &operator=(const FileSet &other)
2766         { assign(other); return *this; }
2768     /**
2769      *
2770      */
2771     virtual ~FileSet()
2772         {}
2774     /**
2775      *
2776      */
2777     String getDirectory()
2778         { return directory; }
2779         
2780     /**
2781      *
2782      */
2783     void setDirectory(const String &val)
2784         { directory = val; }
2786     /**
2787      *
2788      */
2789     void setFiles(const std::vector<String> &val)
2790         { files = val; }
2792     /**
2793      *
2794      */
2795     std::vector<String> getFiles()
2796         { return files; }
2797         
2798     /**
2799      *
2800      */
2801     void setIncludes(const std::vector<String> &val)
2802         { includes = val; }
2804     /**
2805      *
2806      */
2807     std::vector<String> getIncludes()
2808         { return includes; }
2809         
2810     /**
2811      *
2812      */
2813     void setExcludes(const std::vector<String> &val)
2814         { excludes = val; }
2816     /**
2817      *
2818      */
2819     std::vector<String> getExcludes()
2820         { return excludes; }
2821         
2822     /**
2823      *
2824      */
2825     unsigned int size()
2826         { return files.size(); }
2827         
2828     /**
2829      *
2830      */
2831     String operator[](int index)
2832         { return files[index]; }
2833         
2834     /**
2835      *
2836      */
2837     void clear()
2838         {
2839         directory = "";
2840         files.clear();
2841         includes.clear();
2842         excludes.clear();
2843         }
2844         
2846 private:
2848     void assign(const FileSet &other)
2849         {
2850         directory = other.directory;
2851         files     = other.files;
2852         includes  = other.includes;
2853         excludes  = other.excludes;
2854         }
2856     String directory;
2857     std::vector<String> files;
2858     std::vector<String> includes;
2859     std::vector<String> excludes;
2860 };
2865 //########################################################################
2866 //# M A K E    B A S E
2867 //########################################################################
2868 /**
2869  * Base class for all classes in this file
2870  */
2871 class MakeBase
2873 public:
2875     MakeBase()
2876         { line = 0; }
2877     virtual ~MakeBase()
2878         {}
2880     /**
2881      *     Return the URI of the file associated with this object 
2882      */     
2883     URI getURI()
2884         { return uri; }
2886     /**
2887      * Set the uri to the given string
2888      */
2889     void setURI(const String &uristr)
2890         { uri.parse(uristr); }
2892     /**
2893      *  Resolve another path relative to this one
2894      */
2895     String resolve(const String &otherPath);
2897     /**
2898      *  Get an element attribute, performing substitutions if necessary
2899      */
2900     bool getAttribute(Element *elem, const String &name, String &result);
2902     /**
2903      * Get an element value, performing substitutions if necessary
2904      */
2905     bool getValue(Element *elem, String &result);
2906     
2907     /**
2908      * Set the current line number in the file
2909      */         
2910     void setLine(int val)
2911         { line = val; }
2912         
2913     /**
2914      * Get the current line number in the file
2915      */         
2916     int getLine()
2917         { return line; }
2919 protected:
2921     /**
2922      *    The path to the file associated with this object
2923      */     
2924     URI uri;
2927     /**
2928      *  Print a printf()-like formatted error message
2929      */
2930     void error(char *fmt, ...);
2932     /**
2933      *  Print a printf()-like formatted trace message
2934      */
2935     void status(char *fmt, ...);
2937     /**
2938      *  Print a printf()-like formatted trace message
2939      */
2940     void trace(char *fmt, ...);
2942     /**
2943      *  Check if a given string matches a given regex pattern
2944      */
2945     bool regexMatch(const String &str, const String &pattern);
2947     /**
2948      *
2949      */
2950     String getSuffix(const String &fname);
2952     /**
2953      * Break up a string into substrings delimited the characters
2954      * in delimiters.  Null-length substrings are ignored
2955      */  
2956     std::vector<String> tokenize(const String &val,
2957                           const String &delimiters);
2959     /**
2960      *  replace runs of whitespace with a space
2961      */
2962     String strip(const String &s);
2964     /**
2965      *  remove leading whitespace from each line
2966      */
2967     String leftJustify(const String &s);
2969     /**
2970      *  remove leading and trailing whitespace from string
2971      */
2972     String trim(const String &s);
2974     /**
2975      * Return the native format of the canonical
2976      * path which we store
2977      */
2978     String getNativePath(const String &path);
2980     /**
2981      * Execute a shell command.  Outbuf is a ref to a string
2982      * to catch the result.     
2983      */         
2984     bool executeCommand(const String &call,
2985                         const String &inbuf,
2986                         String &outbuf,
2987                         String &errbuf);
2988     /**
2989      * List all directories in a given base and starting directory
2990      * It is usually called like:
2991      *        bool ret = listDirectories("src", "", result);    
2992      */         
2993     bool listDirectories(const String &baseName,
2994                          const String &dirname,
2995                          std::vector<String> &res);
2997     /**
2998      * Find all files in the named directory 
2999      */         
3000     bool listFiles(const String &baseName,
3001                    const String &dirname,
3002                    std::vector<String> &result);
3004     /**
3005      * Perform a listing for a fileset 
3006      */         
3007     bool listFiles(MakeBase &propRef, FileSet &fileSet);
3009     /**
3010      * Parse a <patternset>
3011      */  
3012     bool parsePatternSet(Element *elem,
3013                        MakeBase &propRef,
3014                        std::vector<String> &includes,
3015                        std::vector<String> &excludes);
3017     /**
3018      * Parse a <fileset> entry, and determine which files
3019      * should be included
3020      */  
3021     bool parseFileSet(Element *elem,
3022                     MakeBase &propRef,
3023                     FileSet &fileSet);
3025     /**
3026      * Return this object's property list
3027      */
3028     virtual std::map<String, String> &getProperties()
3029         { return properties; }
3031     /**
3032      * Return a named property if found, else a null string
3033      */
3034     virtual String getProperty(const String &name)
3035         {
3036         String val;
3037         std::map<String, String>::iterator iter;
3038         iter = properties.find(name);
3039         if (iter != properties.end())
3040             val = iter->second;
3041         return val;
3042         }
3045     std::map<String, String> properties;
3047     /**
3048      * Turn 'true' and 'false' into boolean values
3049      */             
3050     bool getBool(const String &str, bool &val);
3052     /**
3053      * Create a directory, making intermediate dirs
3054      * if necessary
3055      */                  
3056     bool createDirectory(const String &dirname);
3058     /**
3059      * Delete a directory and its children if desired
3060      */
3061     bool removeDirectory(const String &dirName);
3063     /**
3064      * Copy a file from one name to another. Perform only if needed
3065      */ 
3066     bool copyFile(const String &srcFile, const String &destFile);
3068     /**
3069      * Tests if the file exists and is a regular file
3070      */ 
3071     bool isRegularFile(const String &fileName);
3073     /**
3074      * Tests if the file exists and is a directory
3075      */ 
3076     bool isDirectory(const String &fileName);
3078     /**
3079      * Tests is the modification date of fileA is newer than fileB
3080      */ 
3081     bool isNewerThan(const String &fileA, const String &fileB);
3083 private:
3085     /**
3086      * replace variable refs like ${a} with their values
3087      */         
3088     bool getSubstitutions(const String &s, String &result);
3090     int line;
3093 };
3098 /**
3099  *  Print a printf()-like formatted error message
3100  */
3101 void MakeBase::error(char *fmt, ...)
3103     va_list args;
3104     va_start(args,fmt);
3105     fprintf(stderr, "Make error line %d: ", line);
3106     vfprintf(stderr, fmt, args);
3107     fprintf(stderr, "\n");
3108     va_end(args) ;
3113 /**
3114  *  Print a printf()-like formatted trace message
3115  */
3116 void MakeBase::status(char *fmt, ...)
3118     va_list args;
3119     va_start(args,fmt);
3120     //fprintf(stdout, " ");
3121     vfprintf(stdout, fmt, args);
3122     fprintf(stdout, "\n");
3123     va_end(args) ;
3128 /**
3129  *  Resolve another path relative to this one
3130  */
3131 String MakeBase::resolve(const String &otherPath)
3133     URI otherURI(otherPath);
3134     URI fullURI = uri.resolve(otherURI);
3135     String ret = fullURI.toString();
3136     return ret;
3140 /**
3141  *  Print a printf()-like formatted trace message
3142  */
3143 void MakeBase::trace(char *fmt, ...)
3145     va_list args;
3146     va_start(args,fmt);
3147     fprintf(stdout, "Make: ");
3148     vfprintf(stdout, fmt, args);
3149     fprintf(stdout, "\n");
3150     va_end(args) ;
3155 /**
3156  *  Check if a given string matches a given regex pattern
3157  */
3158 bool MakeBase::regexMatch(const String &str, const String &pattern)
3160     const TRexChar *terror = NULL;
3161     const TRexChar *cpat = pattern.c_str();
3162     TRex *expr = trex_compile(cpat, &terror);
3163     if (!expr)
3164         {
3165         if (!terror)
3166             terror = "undefined";
3167         error("compilation error [%s]!\n", terror);
3168         return false;
3169         } 
3171     bool ret = true;
3173     const TRexChar *cstr = str.c_str();
3174     if (trex_match(expr, cstr))
3175         {
3176         ret = true;
3177         }
3178     else
3179         {
3180         ret = false;
3181         }
3183     trex_free(expr);
3185     return ret;
3188 /**
3189  *  Return the suffix, if any, of a file name
3190  */
3191 String MakeBase::getSuffix(const String &fname)
3193     if (fname.size() < 2)
3194         return "";
3195     unsigned int pos = fname.find_last_of('.');
3196     if (pos == fname.npos)
3197         return "";
3198     pos++;
3199     String res = fname.substr(pos, fname.size()-pos);
3200     //trace("suffix:%s", res.c_str()); 
3201     return res;
3206 /**
3207  * Break up a string into substrings delimited the characters
3208  * in delimiters.  Null-length substrings are ignored
3209  */  
3210 std::vector<String> MakeBase::tokenize(const String &str,
3211                                 const String &delimiters)
3214     std::vector<String> res;
3215     char *del = (char *)delimiters.c_str();
3216     String dmp;
3217     for (unsigned int i=0 ; i<str.size() ; i++)
3218         {
3219         char ch = str[i];
3220         char *p = (char *)0;
3221         for (p=del ; *p ; p++)
3222             if (*p == ch)
3223                 break;
3224         if (*p)
3225             {
3226             if (dmp.size() > 0)
3227                 {
3228                 res.push_back(dmp);
3229                 dmp.clear();
3230                 }
3231             }
3232         else
3233             {
3234             dmp.push_back(ch);
3235             }
3236         }
3237     //Add tail
3238     if (dmp.size() > 0)
3239         {
3240         res.push_back(dmp);
3241         dmp.clear();
3242         }
3244     return res;
3249 /**
3250  *  replace runs of whitespace with a single space
3251  */
3252 String MakeBase::strip(const String &s)
3254     int len = s.size();
3255     String stripped;
3256     for (int i = 0 ; i<len ; i++)
3257         {
3258         char ch = s[i];
3259         if (isspace(ch))
3260             {
3261             stripped.push_back(' ');
3262             for ( ; i<len ; i++)
3263                 {
3264                 ch = s[i];
3265                 if (!isspace(ch))
3266                     {
3267                     stripped.push_back(ch);
3268                     break;
3269                     }
3270                 }
3271             }
3272         else
3273             {
3274             stripped.push_back(ch);
3275             }
3276         }
3277     return stripped;
3280 /**
3281  *  remove leading whitespace from each line
3282  */
3283 String MakeBase::leftJustify(const String &s)
3285     String out;
3286     int len = s.size();
3287     for (int i = 0 ; i<len ; )
3288         {
3289         char ch;
3290         //Skip to first visible character
3291         while (i<len)
3292             {
3293             ch = s[i];
3294             if (ch == '\n' || ch == '\r'
3295               || !isspace(ch))
3296                   break;
3297             i++;
3298             }
3299         //Copy the rest of the line
3300         while (i<len)
3301             {
3302             ch = s[i];
3303             if (ch == '\n' || ch == '\r')
3304                 {
3305                 if (ch != '\r')
3306                     out.push_back('\n');
3307                 i++;
3308                 break;
3309                 }
3310             else
3311                 {
3312                 out.push_back(ch);
3313                 }
3314             i++;
3315             }
3316         }
3317     return out;
3321 /**
3322  *  Removes whitespace from beginning and end of a string
3323  */
3324 String MakeBase::trim(const String &s)
3326     if (s.size() < 1)
3327         return s;
3328     
3329     //Find first non-ws char
3330     unsigned int begin = 0;
3331     for ( ; begin < s.size() ; begin++)
3332         {
3333         if (!isspace(s[begin]))
3334             break;
3335         }
3337     //Find first non-ws char, going in reverse
3338     unsigned int end = s.size() - 1;
3339     for ( ; end > begin ; end--)
3340         {
3341         if (!isspace(s[end]))
3342             break;
3343         }
3344     //trace("begin:%d  end:%d", begin, end);
3346     String res = s.substr(begin, end-begin+1);
3347     return res;
3350 /**
3351  * Return the native format of the canonical
3352  * path which we store
3353  */
3354 String MakeBase::getNativePath(const String &path)
3356 #ifdef __WIN32__
3357     String npath;
3358     unsigned int firstChar = 0;
3359     if (path.size() >= 3)
3360         {
3361         if (path[0] == '/' &&
3362             isalpha(path[1]) &&
3363             path[2] == ':')
3364             firstChar++;
3365         }
3366     for (unsigned int i=firstChar ; i<path.size() ; i++)
3367         {
3368         char ch = path[i];
3369         if (ch == '/')
3370             npath.push_back('\\');
3371         else
3372             npath.push_back(ch);
3373         }
3374     return npath;
3375 #else
3376     return path;
3377 #endif
3381 #ifdef __WIN32__
3382 #include <tchar.h>
3384 static String win32LastError()
3387     DWORD dw = GetLastError(); 
3389     LPVOID str;
3390     FormatMessage(
3391         FORMAT_MESSAGE_ALLOCATE_BUFFER | 
3392         FORMAT_MESSAGE_FROM_SYSTEM,
3393         NULL,
3394         dw,
3395         0,
3396         (LPTSTR) &str,
3397         0, NULL );
3398     LPTSTR p = _tcschr((const char *)str, _T('\r'));
3399     if(p != NULL)
3400         { // lose CRLF
3401         *p = _T('\0');
3402         }
3403     String ret = (char *)str;
3404     LocalFree(str);
3406     return ret;
3408 #endif
3412 /**
3413  * Execute a system call, using pipes to send data to the
3414  * program's stdin,  and reading stdout and stderr.
3415  */
3416 bool MakeBase::executeCommand(const String &command,
3417                               const String &inbuf,
3418                               String &outbuf,
3419                               String &errbuf)
3422     status("============ cmd ============\n%s\n=============================",
3423                 command.c_str());
3425     outbuf.clear();
3426     errbuf.clear();
3427     
3428 #ifdef __WIN32__
3430     /*
3431     I really hate having win32 code in this program, but the
3432     read buffer in command.com and cmd.exe are just too small
3433     for the large commands we need for compiling and linking.
3434     */
3436     bool ret = true;
3438     //# Allocate a separate buffer for safety
3439     char *paramBuf = new char[command.size() + 1];
3440     if (!paramBuf)
3441        {
3442        error("executeCommand cannot allocate command buffer");
3443        return false;
3444        }
3445     strcpy(paramBuf, (char *)command.c_str());
3447     //# Go to http://msdn2.microsoft.com/en-us/library/ms682499.aspx
3448     //# to see how Win32 pipes work
3450     //# Create pipes
3451     SECURITY_ATTRIBUTES saAttr; 
3452     saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); 
3453     saAttr.bInheritHandle = TRUE; 
3454     saAttr.lpSecurityDescriptor = NULL; 
3455     HANDLE stdinRead,  stdinWrite;
3456     HANDLE stdoutRead, stdoutWrite;
3457     HANDLE stderrRead, stderrWrite;
3458     if (!CreatePipe(&stdinRead, &stdinWrite, &saAttr, 0))
3459         {
3460         error("executeProgram: could not create pipe");
3461         delete[] paramBuf;
3462         return false;
3463         } 
3464     SetHandleInformation(stdinWrite, HANDLE_FLAG_INHERIT, 0);
3465     if (!CreatePipe(&stdoutRead, &stdoutWrite, &saAttr, 0))
3466         {
3467         error("executeProgram: could not create pipe");
3468         delete[] paramBuf;
3469         return false;
3470         } 
3471     SetHandleInformation(stdoutRead, HANDLE_FLAG_INHERIT, 0);
3472     if (!CreatePipe(&stderrRead, &stderrWrite, &saAttr, 0))
3473         {
3474         error("executeProgram: could not create pipe");
3475         delete[] paramBuf;
3476         return false;
3477         } 
3478     SetHandleInformation(stderrRead, HANDLE_FLAG_INHERIT, 0);
3480     // Create the process
3481     STARTUPINFO siStartupInfo;
3482     PROCESS_INFORMATION piProcessInfo;
3483     memset(&siStartupInfo, 0, sizeof(siStartupInfo));
3484     memset(&piProcessInfo, 0, sizeof(piProcessInfo));
3485     siStartupInfo.cb = sizeof(siStartupInfo);
3486     siStartupInfo.hStdError   =  stderrWrite;
3487     siStartupInfo.hStdOutput  =  stdoutWrite;
3488     siStartupInfo.hStdInput   =  stdinRead;
3489     siStartupInfo.dwFlags    |=  STARTF_USESTDHANDLES;
3490    
3491     if (!CreateProcess(NULL, paramBuf, NULL, NULL, true,
3492                 0, NULL, NULL, &siStartupInfo,
3493                 &piProcessInfo))
3494         {
3495         error("executeCommand : could not create process : %s",
3496                     win32LastError().c_str());
3497         ret = false;
3498         }
3500     delete[] paramBuf;
3502     DWORD bytesWritten;
3503     if (inbuf.size()>0 &&
3504         !WriteFile(stdinWrite, inbuf.c_str(), inbuf.size(), 
3505                &bytesWritten, NULL))
3506         {
3507         error("executeCommand: could not write to pipe");
3508         return false;
3509         }    
3510     if (!CloseHandle(stdinWrite))
3511         {          
3512         error("executeCommand: could not close write pipe");
3513         return false;
3514         }
3515     if (!CloseHandle(stdoutWrite))
3516         {
3517         error("executeCommand: could not close read pipe");
3518         return false;
3519         }
3520     if (!CloseHandle(stderrWrite))
3521         {
3522         error("executeCommand: could not close read pipe");
3523         return false;
3524         }
3526     bool lastLoop = false;
3527     while (true)
3528         {
3529         DWORD avail;
3530         DWORD bytesRead;
3531         char readBuf[4096];
3533         //trace("## stderr");
3534         PeekNamedPipe(stderrRead, NULL, 0, NULL, &avail, NULL);
3535         if (avail > 0)
3536             {
3537             bytesRead = 0;
3538             if (avail>4096) avail = 4096;
3539             ReadFile(stderrRead, readBuf, avail, &bytesRead, NULL);
3540             if (bytesRead > 0)
3541                 {
3542                 for (unsigned int i=0 ; i<bytesRead ; i++)
3543                     errbuf.push_back(readBuf[i]);
3544                 }
3545             }
3547         //trace("## stdout");
3548         PeekNamedPipe(stdoutRead, NULL, 0, NULL, &avail, NULL);
3549         if (avail > 0)
3550             {
3551             bytesRead = 0;
3552             if (avail>4096) avail = 4096;
3553             ReadFile(stdoutRead, readBuf, avail, &bytesRead, NULL);
3554             if (bytesRead > 0)
3555                 {
3556                 for (unsigned int i=0 ; i<bytesRead ; i++)
3557                     outbuf.push_back(readBuf[i]);
3558                 }
3559             }
3560             
3561         //Was this the final check after program done?
3562         if (lastLoop)
3563             break;
3565         DWORD exitCode;
3566         GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
3567         if (exitCode != STILL_ACTIVE)
3568             lastLoop = true;
3570         Sleep(50);
3571         }    
3572     //trace("outbuf:%s", outbuf.c_str());
3573     if (!CloseHandle(stdoutRead))
3574         {
3575         error("executeCommand: could not close read pipe");
3576         return false;
3577         }
3578     if (!CloseHandle(stderrRead))
3579         {
3580         error("executeCommand: could not close read pipe");
3581         return false;
3582         }
3584     DWORD exitCode;
3585     GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
3586     //trace("exit code:%d", exitCode);
3587     if (exitCode != 0)
3588         {
3589         ret = false;
3590         }
3591     
3592     CloseHandle(piProcessInfo.hProcess);
3593     CloseHandle(piProcessInfo.hThread);
3595     return ret;
3597 #else //do it unix-style
3599     String s;
3600     FILE *f = popen(command.c_str(), "r");
3601     int errnum = 0;
3602     if (f)
3603         {
3604         while (true)
3605             {
3606             int ch = fgetc(f);
3607             if (ch < 0)
3608                 break;
3609             s.push_back((char)ch);
3610             }
3611         errnum = pclose(f);
3612         }
3613     outbuf = s;
3614     if (errnum != 0)
3615         {
3616         error("exec of command '%s' failed : %s",
3617              command.c_str(), strerror(errno));
3618         return false;
3619         }
3620     else
3621         return true;
3623 #endif
3624
3629 bool MakeBase::listDirectories(const String &baseName,
3630                               const String &dirName,
3631                               std::vector<String> &res)
3633     res.push_back(dirName);
3634     String fullPath = baseName;
3635     if (dirName.size()>0)
3636         {
3637         fullPath.append("/");
3638         fullPath.append(dirName);
3639         }
3640     DIR *dir = opendir(fullPath.c_str());
3641     while (true)
3642         {
3643         struct dirent *de = readdir(dir);
3644         if (!de)
3645             break;
3647         //Get the directory member name
3648         String s = de->d_name;
3649         if (s.size() == 0 || s[0] == '.')
3650             continue;
3651         String childName = dirName;
3652         childName.append("/");
3653         childName.append(s);
3655         String fullChildPath = baseName;
3656         fullChildPath.append("/");
3657         fullChildPath.append(childName);
3658         struct stat finfo;
3659         String childNative = getNativePath(fullChildPath);
3660         if (stat(childNative.c_str(), &finfo)<0)
3661             {
3662             error("cannot stat file:%s", childNative.c_str());
3663             }
3664         else if (S_ISDIR(finfo.st_mode))
3665             {
3666             //trace("directory: %s", childName.c_str());
3667             if (!listDirectories(baseName, childName, res))
3668                 return false;
3669             }
3670         }
3671     closedir(dir);
3673     return true;
3677 bool MakeBase::listFiles(const String &baseDir,
3678                          const String &dirName,
3679                          std::vector<String> &res)
3681     String fullDir = baseDir;
3682     if (dirName.size()>0)
3683         {
3684         fullDir.append("/");
3685         fullDir.append(dirName);
3686         }
3687     String dirNative = getNativePath(fullDir);
3689     std::vector<String> subdirs;
3690     DIR *dir = opendir(dirNative.c_str());
3691     if (!dir)
3692         {
3693         error("Could not open directory %s : %s",
3694               dirNative.c_str(), strerror(errno));
3695         return false;
3696         }
3697     while (true)
3698         {
3699         struct dirent *de = readdir(dir);
3700         if (!de)
3701             break;
3703         //Get the directory member name
3704         String s = de->d_name;
3705         if (s.size() == 0 || s[0] == '.')
3706             continue;
3707         String childName;
3708         if (dirName.size()>0)
3709             {
3710             childName.append(dirName);
3711             childName.append("/");
3712             }
3713         childName.append(s);
3714         String fullChild = baseDir;
3715         fullChild.append("/");
3716         fullChild.append(childName);
3717         
3718         if (isDirectory(fullChild))
3719             {
3720             //trace("directory: %s", childName.c_str());
3721             if (!listFiles(baseDir, childName, res))
3722                 return false;
3723             continue;
3724             }
3725         else if (!isRegularFile(fullChild))
3726             {
3727             error("unknown file:%s", childName.c_str());
3728             return false;
3729             }
3731        //all done!
3732         res.push_back(childName);
3734         }
3735     closedir(dir);
3737     return true;
3741 bool MakeBase::listFiles(MakeBase &propRef, FileSet &fileSet)
3743     String baseDir = propRef.resolve(fileSet.getDirectory());
3744     std::vector<String> fileList;
3745     if (!listFiles(baseDir, "", fileList))
3746         return false;
3748     std::vector<String> includes = fileSet.getIncludes();
3749     std::vector<String> excludes = fileSet.getExcludes();
3751     std::vector<String> incs;
3752     std::vector<String>::iterator iter;
3754     std::sort(fileList.begin(), fileList.end());
3756     //If there are <includes>, then add files to the output
3757     //in the order of the include list
3758     if (includes.size()==0)
3759         incs = fileList;
3760     else
3761         {
3762         for (iter = includes.begin() ; iter != includes.end() ; iter++)
3763             {
3764             String pattern = *iter;
3765             std::vector<String>::iterator siter;
3766             for (siter = fileList.begin() ; siter != fileList.end() ; siter++)
3767                 {
3768                 String s = *siter;
3769                 if (regexMatch(s, pattern))
3770                     {
3771                     //trace("INCLUDED:%s", s.c_str());
3772                     incs.push_back(s);
3773                     }
3774                 }
3775             }
3776         }
3778     //Now trim off the <excludes>
3779     std::vector<String> res;
3780     for (iter = incs.begin() ; iter != incs.end() ; iter++)
3781         {
3782         String s = *iter;
3783         bool skipme = false;
3784         std::vector<String>::iterator siter;
3785         for (siter = excludes.begin() ; siter != excludes.end() ; siter++)
3786             {
3787             String pattern = *siter;
3788             if (regexMatch(s, pattern))
3789                 {
3790                 //trace("EXCLUDED:%s", s.c_str());
3791                 skipme = true;
3792                 break;
3793                 }
3794             }
3795         if (!skipme)
3796             res.push_back(s);
3797         }
3798         
3799     fileSet.setFiles(res);
3801     return true;
3808 bool MakeBase::getSubstitutions(const String &str, String &result)
3810     String s = trim(str);
3811     int len = (int)s.size();
3812     String val;
3813     for (int i=0 ; i<len ; i++)
3814         {
3815         char ch = s[i];
3816         if (ch == '$' && s[i+1] == '{')
3817             {
3818             String varname;
3819             int j = i+2;
3820             for ( ; j<len ; j++)
3821                 {
3822                 ch = s[j];
3823                 if (ch == '$' && s[j+1] == '{')
3824                     {
3825                     error("attribute %s cannot have nested variable references",
3826                            s.c_str());
3827                     return false;
3828                     }
3829                 else if (ch == '}')
3830                     {
3831                     std::map<String, String>::iterator iter;
3832                     iter = properties.find(trim(varname));
3833                     if (iter != properties.end())
3834                         {
3835                         val.append(iter->second);
3836                         }
3837                     else
3838                         {
3839                         error("property ${%s} not found", varname.c_str());
3840                         return false;
3841                         }
3842                     break;
3843                     }
3844                 else
3845                     {
3846                     varname.push_back(ch);
3847                     }
3848                 }
3849             i = j;
3850             }
3851         else
3852             {
3853             val.push_back(ch);
3854             }
3855         }
3856     result = val;
3857     return true;
3861 bool MakeBase::getAttribute(Element *elem, const String &name,
3862                                     String &result)
3864     String s = elem->getAttribute(name);
3865     return getSubstitutions(s, result);
3869 bool MakeBase::getValue(Element *elem, String &result)
3871     String s = elem->getValue();
3872     //Replace all runs of whitespace with a single space
3873     return getSubstitutions(s, result);
3877 /**
3878  * Turn 'true' and 'false' into boolean values
3879  */             
3880 bool MakeBase::getBool(const String &str, bool &val)
3882     if (str == "true")
3883         val = true;
3884     else if (str == "false")
3885         val = false;
3886     else
3887         {
3888         error("expected 'true' or 'false'.  found '%s'", str.c_str());
3889         return false;
3890         }
3891     return true;
3897 /**
3898  * Parse a <patternset> entry
3899  */  
3900 bool MakeBase::parsePatternSet(Element *elem,
3901                           MakeBase &propRef,
3902                           std::vector<String> &includes,
3903                           std::vector<String> &excludes
3904                           )
3906     std::vector<Element *> children  = elem->getChildren();
3907     for (unsigned int i=0 ; i<children.size() ; i++)
3908         {
3909         Element *child = children[i];
3910         String tagName = child->getName();
3911         if (tagName == "exclude")
3912             {
3913             String fname;
3914             if (!propRef.getAttribute(child, "name", fname))
3915                 return false;
3916             //trace("EXCLUDE: %s", fname.c_str());
3917             excludes.push_back(fname);
3918             }
3919         else if (tagName == "include")
3920             {
3921             String fname;
3922             if (!propRef.getAttribute(child, "name", fname))
3923                 return false;
3924             //trace("INCLUDE: %s", fname.c_str());
3925             includes.push_back(fname);
3926             }
3927         }
3929     return true;
3935 /**
3936  * Parse a <fileset> entry, and determine which files
3937  * should be included
3938  */  
3939 bool MakeBase::parseFileSet(Element *elem,
3940                           MakeBase &propRef,
3941                           FileSet &fileSet)
3943     String name = elem->getName();
3944     if (name != "fileset")
3945         {
3946         error("expected <fileset>");
3947         return false;
3948         }
3951     std::vector<String> includes;
3952     std::vector<String> excludes;
3954     //A fileset has one implied patternset
3955     if (!parsePatternSet(elem, propRef, includes, excludes))
3956         {
3957         return false;
3958         }
3959     //Look for child tags, including more patternsets
3960     std::vector<Element *> children  = elem->getChildren();
3961     for (unsigned int i=0 ; i<children.size() ; i++)
3962         {
3963         Element *child = children[i];
3964         String tagName = child->getName();
3965         if (tagName == "patternset")
3966             {
3967             if (!parsePatternSet(child, propRef, includes, excludes))
3968                 {
3969                 return false;
3970                 }
3971             }
3972         }
3974     String dir;
3975     //Now do the stuff
3976     //Get the base directory for reading file names
3977     if (!propRef.getAttribute(elem, "dir", dir))
3978         return false;
3980     fileSet.setDirectory(dir);
3981     fileSet.setIncludes(includes);
3982     fileSet.setExcludes(excludes);
3983     
3984     /*
3985     std::vector<String> fileList;
3986     if (dir.size() > 0)
3987         {
3988         String baseDir = propRef.resolve(dir);
3989         if (!listFiles(baseDir, "", includes, excludes, fileList))
3990             return false;
3991         }
3992     std::sort(fileList.begin(), fileList.end());
3993     result = fileList;
3994     */
3996     
3997     /*
3998     for (unsigned int i=0 ; i<result.size() ; i++)
3999         {
4000         trace("RES:%s", result[i].c_str());
4001         }
4002     */
4004     
4005     return true;
4010 /**
4011  * Create a directory, making intermediate dirs
4012  * if necessary
4013  */                  
4014 bool MakeBase::createDirectory(const String &dirname)
4016     //trace("## createDirectory: %s", dirname.c_str());
4017     //## first check if it exists
4018     struct stat finfo;
4019     String nativeDir = getNativePath(dirname);
4020     char *cnative = (char *) nativeDir.c_str();
4021 #ifdef __WIN32__
4022     if (strlen(cnative)==2 && cnative[1]==':')
4023         return true;
4024 #endif
4025     if (stat(cnative, &finfo)==0)
4026         {
4027         if (!S_ISDIR(finfo.st_mode))
4028             {
4029             error("mkdir: file %s exists but is not a directory",
4030                   cnative);
4031             return false;
4032             }
4033         else //exists
4034             {
4035             return true;
4036             }
4037         }
4039     //## 2: pull off the last path segment, if any,
4040     //## to make the dir 'above' this one, if necessary
4041     unsigned int pos = dirname.find_last_of('/');
4042     if (pos>0 && pos != dirname.npos)
4043         {
4044         String subpath = dirname.substr(0, pos);
4045         //A letter root (c:) ?
4046         if (!createDirectory(subpath))
4047             return false;
4048         }
4049         
4050     //## 3: now make
4051 #ifdef __WIN32__
4052     if (mkdir(cnative)<0)
4053 #else
4054     if (mkdir(cnative, S_IRWXU | S_IRWXG | S_IRWXO)<0)
4055 #endif
4056         {
4057         error("cannot make directory '%s' : %s",
4058                  cnative, strerror(errno));
4059         return false;
4060         }
4061         
4062     return true;
4066 /**
4067  * Remove a directory recursively
4068  */ 
4069 bool MakeBase::removeDirectory(const String &dirName)
4071     char *dname = (char *)dirName.c_str();
4073     DIR *dir = opendir(dname);
4074     if (!dir)
4075         {
4076         //# Let this fail nicely.
4077         return true;
4078         //error("error opening directory %s : %s", dname, strerror(errno));
4079         //return false;
4080         }
4081     
4082     while (true)
4083         {
4084         struct dirent *de = readdir(dir);
4085         if (!de)
4086             break;
4088         //Get the directory member name
4089         String s = de->d_name;
4090         if (s.size() == 0 || s[0] == '.')
4091             continue;
4092         String childName;
4093         if (dirName.size() > 0)
4094             {
4095             childName.append(dirName);
4096             childName.append("/");
4097             }
4098         childName.append(s);
4101         struct stat finfo;
4102         String childNative = getNativePath(childName);
4103         char *cnative = (char *)childNative.c_str();
4104         if (stat(cnative, &finfo)<0)
4105             {
4106             error("cannot stat file:%s", cnative);
4107             }
4108         else if (S_ISDIR(finfo.st_mode))
4109             {
4110             //trace("DEL dir: %s", childName.c_str());
4111             if (!removeDirectory(childName))
4112                 {
4113                 return false;
4114                 }
4115             }
4116         else if (!S_ISREG(finfo.st_mode))
4117             {
4118             //trace("not regular: %s", cnative);
4119             }
4120         else
4121             {
4122             //trace("DEL file: %s", childName.c_str());
4123             if (remove(cnative)<0)
4124                 {
4125                 error("error deleting %s : %s",
4126                      cnative, strerror(errno));
4127                 return false;
4128                 }
4129             }
4130         }
4131     closedir(dir);
4133     //Now delete the directory
4134     String native = getNativePath(dirName);
4135     if (rmdir(native.c_str())<0)
4136         {
4137         error("could not delete directory %s : %s",
4138             native.c_str() , strerror(errno));
4139         return false;
4140         }
4142     return true;
4143     
4147 /**
4148  * Copy a file from one name to another. Perform only if needed
4149  */ 
4150 bool MakeBase::copyFile(const String &srcFile, const String &destFile)
4152     //# 1 Check up-to-date times
4153     String srcNative = getNativePath(srcFile);
4154     struct stat srcinfo;
4155     if (stat(srcNative.c_str(), &srcinfo)<0)
4156         {
4157         error("source file %s for copy does not exist",
4158                  srcNative.c_str());
4159         return false;
4160         }
4162     String destNative = getNativePath(destFile);
4163     struct stat destinfo;
4164     if (stat(destNative.c_str(), &destinfo)==0)
4165         {
4166         if (destinfo.st_mtime >= srcinfo.st_mtime)
4167             return true;
4168         }
4169         
4170     //# 2 prepare a destination directory if necessary
4171     unsigned int pos = destFile.find_last_of('/');
4172     if (pos != destFile.npos)
4173         {
4174         String subpath = destFile.substr(0, pos);
4175         if (!createDirectory(subpath))
4176             return false;
4177         }
4179     //# 3 do the data copy
4180 #ifndef __WIN32__
4182     FILE *srcf = fopen(srcNative.c_str(), "rb");
4183     if (!srcf)
4184         {
4185         error("copyFile cannot open '%s' for reading", srcNative.c_str());
4186         return false;
4187         }
4188     FILE *destf = fopen(destNative.c_str(), "wb");
4189     if (!destf)
4190         {
4191         error("copyFile cannot open %s for writing", srcNative.c_str());
4192         return false;
4193         }
4195     while (!feof(srcf))
4196         {
4197         int ch = fgetc(srcf);
4198         if (ch<0)
4199             break;
4200         fputc(ch, destf);
4201         }
4203     fclose(destf);
4204     fclose(srcf);
4206 #else
4207     
4208     if (!CopyFile(srcNative.c_str(), destNative.c_str(), false))
4209         {
4210         error("copyFile from %s to %s failed",
4211              srcNative.c_str(), destNative.c_str());
4212         return false;
4213         }
4214         
4215 #endif /* __WIN32__ */
4218     return true;
4223 /**
4224  * Tests if the file exists and is a regular file
4225  */ 
4226 bool MakeBase::isRegularFile(const String &fileName)
4228     String native = getNativePath(fileName);
4229     struct stat finfo;
4230     
4231     //Exists?
4232     if (stat(native.c_str(), &finfo)<0)
4233         return false;
4236     //check the file mode
4237     if (!S_ISREG(finfo.st_mode))
4238         return false;
4240     return true;
4243 /**
4244  * Tests if the file exists and is a directory
4245  */ 
4246 bool MakeBase::isDirectory(const String &fileName)
4248     String native = getNativePath(fileName);
4249     struct stat finfo;
4250     
4251     //Exists?
4252     if (stat(native.c_str(), &finfo)<0)
4253         return false;
4256     //check the file mode
4257     if (!S_ISDIR(finfo.st_mode))
4258         return false;
4260     return true;
4265 /**
4266  * Tests is the modification of fileA is newer than fileB
4267  */ 
4268 bool MakeBase::isNewerThan(const String &fileA, const String &fileB)
4270     //trace("isNewerThan:'%s' , '%s'", fileA.c_str(), fileB.c_str());
4271     String nativeA = getNativePath(fileA);
4272     struct stat infoA;
4273     //IF source does not exist, NOT newer
4274     if (stat(nativeA.c_str(), &infoA)<0)
4275         {
4276         return false;
4277         }
4279     String nativeB = getNativePath(fileB);
4280     struct stat infoB;
4281     //IF dest does not exist, YES, newer
4282     if (stat(nativeB.c_str(), &infoB)<0)
4283         {
4284         return true;
4285         }
4287     //check the actual times
4288     if (infoA.st_mtime > infoB.st_mtime)
4289         {
4290         return true;
4291         }
4293     return false;
4297 //########################################################################
4298 //# P K G    C O N F I G
4299 //########################################################################
4301 /**
4302  *
4303  */
4304 class PkgConfig : public MakeBase
4307 public:
4309     /**
4310      *
4311      */
4312     PkgConfig()
4313         { init(); }
4315     /**
4316      *
4317      */
4318     PkgConfig(const String &namearg)
4319         { init(); name = namearg; }
4321     /**
4322      *
4323      */
4324     PkgConfig(const PkgConfig &other)
4325         { assign(other); }
4327     /**
4328      *
4329      */
4330     PkgConfig &operator=(const PkgConfig &other)
4331         { assign(other); return *this; }
4333     /**
4334      *
4335      */
4336     virtual ~PkgConfig()
4337         { }
4339     /**
4340      *
4341      */
4342     virtual String getName()
4343         { return name; }
4345     /**
4346      *
4347      */
4348     virtual String getDescription()
4349         { return description; }
4351     /**
4352      *
4353      */
4354     virtual String getCflags()
4355         { return cflags; }
4357     /**
4358      *
4359      */
4360     virtual String getLibs()
4361         { return libs; }
4363     /**
4364      *
4365      */
4366     virtual String getVersion()
4367         { return version; }
4369     /**
4370      *
4371      */
4372     virtual int getMajorVersion()
4373         { return majorVersion; }
4375     /**
4376      *
4377      */
4378     virtual int getMinorVersion()
4379         { return minorVersion; }
4381     /**
4382      *
4383      */
4384     virtual int getMicroVersion()
4385         { return microVersion; }
4387     /**
4388      *
4389      */
4390     virtual std::map<String, String> &getAttributes()
4391         { return attrs; }
4393     /**
4394      *
4395      */
4396     virtual std::vector<String> &getRequireList()
4397         { return requireList; }
4399     virtual bool readFile(const String &fileName);
4401 private:
4403     void init()
4404         {
4405         name         = "";
4406         description  = "";
4407         cflags       = "";
4408         libs         = "";
4409         requires     = "";
4410         version      = "";
4411         majorVersion = 0;
4412         minorVersion = 0;
4413         microVersion = 0;
4414         fileName     = "";
4415         attrs.clear();
4416         requireList.clear();
4417         }
4419     void assign(const PkgConfig &other)
4420         {
4421         name         = other.name;
4422         description  = other.description;
4423         cflags       = other.cflags;
4424         libs         = other.libs;
4425         requires     = other.requires;
4426         version      = other.version;
4427         majorVersion = other.majorVersion;
4428         minorVersion = other.minorVersion;
4429         microVersion = other.microVersion;
4430         fileName     = other.fileName;
4431         attrs        = other.attrs;
4432         requireList  = other.requireList;
4433         }
4437     int get(int pos);
4439     int skipwhite(int pos);
4441     int getword(int pos, String &ret);
4443     void parseRequires();
4445     void parseVersion();
4447     bool parse(const String &buf);
4449     void dumpAttrs();
4451     String name;
4453     String description;
4455     String cflags;
4457     String libs;
4459     String requires;
4461     String version;
4463     int majorVersion;
4465     int minorVersion;
4467     int microVersion;
4469     String fileName;
4471     std::map<String, String> attrs;
4473     std::vector<String> requireList;
4475     char *parsebuf;
4476     int parselen;
4477 };
4480 /**
4481  * Get a character from the buffer at pos.  If out of range,
4482  * return -1 for safety
4483  */
4484 int PkgConfig::get(int pos)
4486     if (pos>parselen)
4487         return -1;
4488     return parsebuf[pos];
4493 /**
4494  *  Skip over all whitespace characters beginning at pos.  Return
4495  *  the position of the first non-whitespace character.
4496  */
4497 int PkgConfig::skipwhite(int pos)
4499     while (pos < parselen)
4500         {
4501         int ch = get(pos);
4502         if (ch < 0)
4503             break;
4504         if (!isspace(ch))
4505             break;
4506         pos++;
4507         }
4508     return pos;
4512 /**
4513  *  Parse the buffer beginning at pos, for a word.  Fill
4514  *  'ret' with the result.  Return the position after the
4515  *  word.
4516  */
4517 int PkgConfig::getword(int pos, String &ret)
4519     while (pos < parselen)
4520         {
4521         int ch = get(pos);
4522         if (ch < 0)
4523             break;
4524         if (!isalnum(ch) && ch != '_' && ch != '-'&& ch != '.')
4525             break;
4526         ret.push_back((char)ch);
4527         pos++;
4528         }
4529     return pos;
4532 void PkgConfig::parseRequires()
4534     if (requires.size() == 0)
4535         return;
4536     parsebuf = (char *)requires.c_str();
4537     parselen = requires.size();
4538     int pos = 0;
4539     while (pos < parselen)
4540         {
4541         pos = skipwhite(pos);
4542         String val;
4543         int pos2 = getword(pos, val);
4544         if (pos2 == pos)
4545             break;
4546         pos = pos2;
4547         //trace("val %s", val.c_str());
4548         requireList.push_back(val);
4549         }
4552 static int getint(const String str)
4554     char *s = (char *)str.c_str();
4555     char *ends = NULL;
4556     long val = strtol(s, &ends, 10);
4557     if (ends == s)
4558         return 0L;
4559     else
4560         return val;
4563 void PkgConfig::parseVersion()
4565     if (version.size() == 0)
4566         return;
4567     String s1, s2, s3;
4568     unsigned int pos = 0;
4569     unsigned int pos2 = version.find('.', pos);
4570     if (pos2 == version.npos)
4571         {
4572         s1 = version;
4573         }
4574     else
4575         {
4576         s1 = version.substr(pos, pos2-pos);
4577         pos = pos2;
4578         pos++;
4579         if (pos < version.size())
4580             {
4581             pos2 = version.find('.', pos);
4582             if (pos2 == version.npos)
4583                 {
4584                 s2 = version.substr(pos, version.size()-pos);
4585                 }
4586             else
4587                 {
4588                 s2 = version.substr(pos, pos2-pos);
4589                 pos = pos2;
4590                 pos++;
4591                 if (pos < version.size())
4592                     s3 = version.substr(pos, pos2-pos);
4593                 }
4594             }
4595         }
4597     majorVersion = getint(s1);
4598     minorVersion = getint(s2);
4599     microVersion = getint(s3);
4600     //trace("version:%d.%d.%d", majorVersion,
4601     //          minorVersion, microVersion );
4605 bool PkgConfig::parse(const String &buf)
4607     init();
4609     parsebuf = (char *)buf.c_str();
4610     parselen = buf.size();
4611     int pos = 0;
4614     while (pos < parselen)
4615         {
4616         String attrName;
4617         pos = skipwhite(pos);
4618         int ch = get(pos);
4619         if (ch == '#')
4620             {
4621             //comment.  eat the rest of the line
4622             while (pos < parselen)
4623                 {
4624                 ch = get(pos);
4625                 if (ch == '\n' || ch < 0)
4626                     break;
4627                 pos++;
4628                 }
4629             continue;
4630             }
4631         pos = getword(pos, attrName);
4632         if (attrName.size() == 0)
4633             continue;
4634         pos = skipwhite(pos);
4635         ch = get(pos);
4636         if (ch != ':' && ch != '=')
4637             {
4638             error("expected ':' or '='");
4639             return false;
4640             }
4641         pos++;
4642         pos = skipwhite(pos);
4643         String attrVal;
4644         while (pos < parselen)
4645             {
4646             ch = get(pos);
4647             if (ch == '\n' || ch < 0)
4648                 break;
4649             else if (ch == '$' && get(pos+1) == '{')
4650                 {
4651                 //#  this is a ${substitution}
4652                 pos += 2;
4653                 String subName;
4654                 while (pos < parselen)
4655                     {
4656                     ch = get(pos);
4657                     if (ch < 0)
4658                         {
4659                         error("unterminated substitution");
4660                         return false;
4661                         }
4662                     else if (ch == '}')
4663                         break;
4664                     else
4665                         subName.push_back((char)ch);
4666                     pos++;
4667                     }
4668                 //trace("subName:%s", subName.c_str());
4669                 String subVal = attrs[subName];
4670                 //trace("subVal:%s", subVal.c_str());
4671                 attrVal.append(subVal);
4672                 }
4673             else
4674                 attrVal.push_back((char)ch);
4675             pos++;
4676             }
4678         attrVal = trim(attrVal);
4679         attrs[attrName] = attrVal;
4681         if (attrName == "Name")
4682             name = attrVal;
4683         else if (attrName == "Description")
4684             description = attrVal;
4685         else if (attrName == "Cflags")
4686             cflags = attrVal;
4687         else if (attrName == "Libs")
4688             libs = attrVal;
4689         else if (attrName == "Requires")
4690             requires = attrVal;
4691         else if (attrName == "Version")
4692             version = attrVal;
4694         //trace("name:'%s'  value:'%s'",
4695         //      attrName.c_str(), attrVal.c_str());
4696         }
4699     parseRequires();
4700     parseVersion();
4702     return true;
4705 void PkgConfig::dumpAttrs()
4707     //trace("### PkgConfig attributes for %s", fileName.c_str());
4708     std::map<String, String>::iterator iter;
4709     for (iter=attrs.begin() ; iter!=attrs.end() ; iter++)
4710         {
4711         trace("   %s = %s", iter->first.c_str(), iter->second.c_str());
4712         }
4716 bool PkgConfig::readFile(const String &fileNameArg)
4718     fileName = fileNameArg;
4720     FILE *f = fopen(fileName.c_str(), "r");
4721     if (!f)
4722         {
4723         error("cannot open file '%s' for reading", fileName.c_str());
4724         return false;
4725         }
4726     String buf;
4727     while (true)
4728         {
4729         int ch = fgetc(f);
4730         if (ch < 0)
4731             break;
4732         buf.push_back((char)ch);
4733         }
4734     fclose(f);
4736     //trace("####### File:\n%s", buf.c_str());
4737     if (!parse(buf))
4738         {
4739         return false;
4740         }
4742     dumpAttrs();
4744     return true;
4751 //########################################################################
4752 //# D E P T O O L
4753 //########################################################################
4757 /**
4758  *  Class which holds information for each file.
4759  */
4760 class FileRec
4762 public:
4764     typedef enum
4765         {
4766         UNKNOWN,
4767         CFILE,
4768         HFILE,
4769         OFILE
4770         } FileType;
4772     /**
4773      *  Constructor
4774      */
4775     FileRec()
4776         {init(); type = UNKNOWN;}
4778     /**
4779      *  Copy constructor
4780      */
4781     FileRec(const FileRec &other)
4782         {init(); assign(other);}
4783     /**
4784      *  Constructor
4785      */
4786     FileRec(int typeVal)
4787         {init(); type = typeVal;}
4788     /**
4789      *  Assignment operator
4790      */
4791     FileRec &operator=(const FileRec &other)
4792         {init(); assign(other); return *this;}
4795     /**
4796      *  Destructor
4797      */
4798     ~FileRec()
4799         {}
4801     /**
4802      *  Directory part of the file name
4803      */
4804     String path;
4806     /**
4807      *  Base name, sans directory and suffix
4808      */
4809     String baseName;
4811     /**
4812      *  File extension, such as cpp or h
4813      */
4814     String suffix;
4816     /**
4817      *  Type of file: CFILE, HFILE, OFILE
4818      */
4819     int type;
4821     /**
4822      * Used to list files ref'd by this one
4823      */
4824     std::map<String, FileRec *> files;
4827 private:
4829     void init()
4830         {
4831         }
4833     void assign(const FileRec &other)
4834         {
4835         type     = other.type;
4836         baseName = other.baseName;
4837         suffix   = other.suffix;
4838         files    = other.files;
4839         }
4841 };
4845 /**
4846  *  Simpler dependency record
4847  */
4848 class DepRec
4850 public:
4852     /**
4853      *  Constructor
4854      */
4855     DepRec()
4856         {init();}
4858     /**
4859      *  Copy constructor
4860      */
4861     DepRec(const DepRec &other)
4862         {init(); assign(other);}
4863     /**
4864      *  Constructor
4865      */
4866     DepRec(const String &fname)
4867         {init(); name = fname; }
4868     /**
4869      *  Assignment operator
4870      */
4871     DepRec &operator=(const DepRec &other)
4872         {init(); assign(other); return *this;}
4875     /**
4876      *  Destructor
4877      */
4878     ~DepRec()
4879         {}
4881     /**
4882      *  Directory part of the file name
4883      */
4884     String path;
4886     /**
4887      *  Base name, without the path and suffix
4888      */
4889     String name;
4891     /**
4892      *  Suffix of the source
4893      */
4894     String suffix;
4897     /**
4898      * Used to list files ref'd by this one
4899      */
4900     std::vector<String> files;
4903 private:
4905     void init()
4906         {
4907         }
4909     void assign(const DepRec &other)
4910         {
4911         path     = other.path;
4912         name     = other.name;
4913         suffix   = other.suffix;
4914         files    = other.files;
4915         }
4917 };
4920 class DepTool : public MakeBase
4922 public:
4924     /**
4925      *  Constructor
4926      */
4927     DepTool()
4928         {init();}
4930     /**
4931      *  Copy constructor
4932      */
4933     DepTool(const DepTool &other)
4934         {init(); assign(other);}
4936     /**
4937      *  Assignment operator
4938      */
4939     DepTool &operator=(const DepTool &other)
4940         {init(); assign(other); return *this;}
4943     /**
4944      *  Destructor
4945      */
4946     ~DepTool()
4947         {}
4950     /**
4951      *  Reset this section of code
4952      */
4953     virtual void init();
4954     
4955     /**
4956      *  Reset this section of code
4957      */
4958     virtual void assign(const DepTool &other)
4959         {
4960         }
4961     
4962     /**
4963      *  Sets the source directory which will be scanned
4964      */
4965     virtual void setSourceDirectory(const String &val)
4966         { sourceDir = val; }
4968     /**
4969      *  Returns the source directory which will be scanned
4970      */
4971     virtual String getSourceDirectory()
4972         { return sourceDir; }
4974     /**
4975      *  Sets the list of files within the directory to analyze
4976      */
4977     virtual void setFileList(const std::vector<String> &list)
4978         { fileList = list; }
4980     /**
4981      * Creates the list of all file names which will be
4982      * candidates for further processing.  Reads make.exclude
4983      * to see which files for directories to leave out.
4984      */
4985     virtual bool createFileList();
4988     /**
4989      *  Generates the forward dependency list
4990      */
4991     virtual bool generateDependencies();
4994     /**
4995      *  Generates the forward dependency list, saving the file
4996      */
4997     virtual bool generateDependencies(const String &);
5000     /**
5001      *  Load a dependency file
5002      */
5003     std::vector<DepRec> loadDepFile(const String &fileName);
5005     /**
5006      *  Load a dependency file, generating one if necessary
5007      */
5008     std::vector<DepRec> getDepFile(const String &fileName,
5009               bool forceRefresh);
5011     /**
5012      *  Save a dependency file
5013      */
5014     bool saveDepFile(const String &fileName);
5017 private:
5020     /**
5021      *
5022      */
5023     void parseName(const String &fullname,
5024                    String &path,
5025                    String &basename,
5026                    String &suffix);
5028     /**
5029      *
5030      */
5031     int get(int pos);
5033     /**
5034      *
5035      */
5036     int skipwhite(int pos);
5038     /**
5039      *
5040      */
5041     int getword(int pos, String &ret);
5043     /**
5044      *
5045      */
5046     bool sequ(int pos, char *key);
5048     /**
5049      *
5050      */
5051     bool addIncludeFile(FileRec *frec, const String &fname);
5053     /**
5054      *
5055      */
5056     bool scanFile(const String &fname, FileRec *frec);
5058     /**
5059      *
5060      */
5061     bool processDependency(FileRec *ofile,
5062                            FileRec *include,
5063                            int depth);
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.  This is the only list that has the actual
5083      * records.  All other lists have pointers to these records.     
5084      */
5085     std::map<String, FileRec *> allFiles;
5087     /**
5088      * The list of .o files, and the
5089      * dependencies upon them.
5090      */
5091     std::map<String, FileRec *> depFiles;
5093     int depFileSize;
5094     char *depFileBuf;
5096     static const int readBufSize = 8192;
5097     char readBuf[8193];//byte larger
5099 };
5105 /**
5106  *  Clean up after processing.  Called by the destructor, but should
5107  *  also be called before the object is reused.
5108  */
5109 void DepTool::init()
5111     sourceDir = ".";
5113     fileList.clear();
5114     directories.clear();
5115     
5116     //clear refs
5117     depFiles.clear();
5118     //clear records
5119     std::map<String, FileRec *>::iterator iter;
5120     for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5121          delete iter->second;
5123     allFiles.clear(); 
5130 /**
5131  *  Parse a full path name into path, base name, and suffix
5132  */
5133 void DepTool::parseName(const String &fullname,
5134                         String &path,
5135                         String &basename,
5136                         String &suffix)
5138     if (fullname.size() < 2)
5139         return;
5141     unsigned int pos = fullname.find_last_of('/');
5142     if (pos != fullname.npos && pos<fullname.size()-1)
5143         {
5144         path = fullname.substr(0, pos);
5145         pos++;
5146         basename = fullname.substr(pos, fullname.size()-pos);
5147         }
5148     else
5149         {
5150         path = "";
5151         basename = fullname;
5152         }
5154     pos = basename.find_last_of('.');
5155     if (pos != basename.npos && pos<basename.size()-1)
5156         {
5157         suffix   = basename.substr(pos+1, basename.size()-pos-1);
5158         basename = basename.substr(0, pos);
5159         }
5161     //trace("parsename:%s %s %s", path.c_str(),
5162     //        basename.c_str(), suffix.c_str()); 
5167 /**
5168  *  Generate our internal file list.
5169  */
5170 bool DepTool::createFileList()
5173     for (unsigned int i=0 ; i<fileList.size() ; i++)
5174         {
5175         String fileName = fileList[i];
5176         //trace("## FileName:%s", fileName.c_str());
5177         String path;
5178         String basename;
5179         String sfx;
5180         parseName(fileName, path, basename, sfx);
5181         if (sfx == "cpp" || sfx == "c" || sfx == "cxx"   ||
5182             sfx == "cc" || sfx == "CC")
5183             {
5184             FileRec *fe         = new FileRec(FileRec::CFILE);
5185             fe->path            = path;
5186             fe->baseName        = basename;
5187             fe->suffix          = sfx;
5188             allFiles[fileName]  = fe;
5189             }
5190         else if (sfx == "h"   ||  sfx == "hh"  ||
5191                  sfx == "hpp" ||  sfx == "hxx")
5192             {
5193             FileRec *fe         = new FileRec(FileRec::HFILE);
5194             fe->path            = path;
5195             fe->baseName        = basename;
5196             fe->suffix          = sfx;
5197             allFiles[fileName]  = fe;
5198             }
5199         }
5201     if (!listDirectories(sourceDir, "", directories))
5202         return false;
5203         
5204     return true;
5211 /**
5212  * Get a character from the buffer at pos.  If out of range,
5213  * return -1 for safety
5214  */
5215 int DepTool::get(int pos)
5217     if (pos>depFileSize)
5218         return -1;
5219     return depFileBuf[pos];
5224 /**
5225  *  Skip over all whitespace characters beginning at pos.  Return
5226  *  the position of the first non-whitespace character.
5227  */
5228 int DepTool::skipwhite(int pos)
5230     while (pos < depFileSize)
5231         {
5232         int ch = get(pos);
5233         if (ch < 0)
5234             break;
5235         if (!isspace(ch))
5236             break;
5237         pos++;
5238         }
5239     return pos;
5243 /**
5244  *  Parse the buffer beginning at pos, for a word.  Fill
5245  *  'ret' with the result.  Return the position after the
5246  *  word.
5247  */
5248 int DepTool::getword(int pos, String &ret)
5250     while (pos < depFileSize)
5251         {
5252         int ch = get(pos);
5253         if (ch < 0)
5254             break;
5255         if (isspace(ch))
5256             break;
5257         ret.push_back((char)ch);
5258         pos++;
5259         }
5260     return pos;
5263 /**
5264  * Return whether the sequence of characters in the buffer
5265  * beginning at pos match the key,  for the length of the key
5266  */
5267 bool DepTool::sequ(int pos, char *key)
5269     while (*key)
5270         {
5271         if (*key != get(pos))
5272             return false;
5273         key++; pos++;
5274         }
5275     return true;
5280 /**
5281  *  Add an include file name to a file record.  If the name
5282  *  is not found in allFiles explicitly, try prepending include
5283  *  directory names to it and try again.
5284  */
5285 bool DepTool::addIncludeFile(FileRec *frec, const String &iname)
5288     std::map<String, FileRec *>::iterator iter =
5289            allFiles.find(iname);
5290     if (iter != allFiles.end()) //already exists
5291         {
5292          //h file in same dir
5293         FileRec *other = iter->second;
5294         //trace("local: '%s'", iname.c_str());
5295         frec->files[iname] = other;
5296         return true;
5297         }
5298     else 
5299         {
5300         //look in other dirs
5301         std::vector<String>::iterator diter;
5302         for (diter=directories.begin() ;
5303              diter!=directories.end() ; diter++)
5304             {
5305             String dfname = *diter;
5306             dfname.append("/");
5307             dfname.append(iname);
5308             iter = allFiles.find(dfname);
5309             if (iter != allFiles.end())
5310                 {
5311                 FileRec *other = iter->second;
5312                 //trace("other: '%s'", iname.c_str());
5313                 frec->files[dfname] = other;
5314                 return true;
5315                 }
5316             }
5317         }
5318     return true;
5323 /**
5324  *  Lightly parse a file to find the #include directives.  Do
5325  *  a bit of state machine stuff to make sure that the directive
5326  *  is valid.  (Like not in a comment).
5327  */
5328 bool DepTool::scanFile(const String &fname, FileRec *frec)
5330     String fileName;
5331     if (sourceDir.size() > 0)
5332         {
5333         fileName.append(sourceDir);
5334         fileName.append("/");
5335         }
5336     fileName.append(fname);
5337     String nativeName = getNativePath(fileName);
5338     FILE *f = fopen(nativeName.c_str(), "r");
5339     if (!f)
5340         {
5341         error("Could not open '%s' for reading", fname.c_str());
5342         return false;
5343         }
5344     String buf;
5345     while (!feof(f))
5346         {
5347         int len = fread(readBuf, 1, readBufSize, f);
5348         readBuf[len] = '\0';
5349         buf.append(readBuf);
5350         }
5351     fclose(f);
5353     depFileSize = buf.size();
5354     depFileBuf  = (char *)buf.c_str();
5355     int pos = 0;
5358     while (pos < depFileSize)
5359         {
5360         //trace("p:%c", get(pos));
5362         //# Block comment
5363         if (get(pos) == '/' && get(pos+1) == '*')
5364             {
5365             pos += 2;
5366             while (pos < depFileSize)
5367                 {
5368                 if (get(pos) == '*' && get(pos+1) == '/')
5369                     {
5370                     pos += 2;
5371                     break;
5372                     }
5373                 else
5374                     pos++;
5375                 }
5376             }
5377         //# Line comment
5378         else if (get(pos) == '/' && get(pos+1) == '/')
5379             {
5380             pos += 2;
5381             while (pos < depFileSize)
5382                 {
5383                 if (get(pos) == '\n')
5384                     {
5385                     pos++;
5386                     break;
5387                     }
5388                 else
5389                     pos++;
5390                 }
5391             }
5392         //# #include! yaay
5393         else if (sequ(pos, "#include"))
5394             {
5395             pos += 8;
5396             pos = skipwhite(pos);
5397             String iname;
5398             pos = getword(pos, iname);
5399             if (iname.size()>2)
5400                 {
5401                 iname = iname.substr(1, iname.size()-2);
5402                 addIncludeFile(frec, iname);
5403                 }
5404             }
5405         else
5406             {
5407             pos++;
5408             }
5409         }
5411     return true;
5416 /**
5417  *  Recursively check include lists to find all files in allFiles to which
5418  *  a given file is dependent.
5419  */
5420 bool DepTool::processDependency(FileRec *ofile,
5421                              FileRec *include,
5422                              int depth)
5424     std::map<String, FileRec *>::iterator iter;
5425     for (iter=include->files.begin() ; iter!=include->files.end() ; iter++)
5426         {
5427         String fname  = iter->first;
5428         if (ofile->files.find(fname) != ofile->files.end())
5429             {
5430             //trace("file '%s' already seen", fname.c_str());
5431             continue;
5432             }
5433         FileRec *child  = iter->second;
5434         ofile->files[fname] = child;
5435       
5436         processDependency(ofile, child, depth+1);
5437         }
5440     return true;
5447 /**
5448  *  Generate the file dependency list.
5449  */
5450 bool DepTool::generateDependencies()
5452     std::map<String, FileRec *>::iterator iter;
5453     //# First pass.  Scan for all includes
5454     for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5455         {
5456         FileRec *frec = iter->second;
5457         if (!scanFile(iter->first, frec))
5458             {
5459             //quit?
5460             }
5461         }
5463     //# Second pass.  Scan for all includes
5464     for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5465         {
5466         FileRec *include = iter->second;
5467         if (include->type == FileRec::CFILE)
5468             {
5469             String cFileName = iter->first;
5470             FileRec *ofile      = new FileRec(FileRec::OFILE);
5471             ofile->path         = include->path;
5472             ofile->baseName     = include->baseName;
5473             ofile->suffix       = include->suffix;
5474             String fname     = include->path;
5475             if (fname.size()>0)
5476                 fname.append("/");
5477             fname.append(include->baseName);
5478             fname.append(".o");
5479             depFiles[fname]    = ofile;
5480             //add the .c file first?   no, don't
5481             //ofile->files[cFileName] = include;
5482             
5483             //trace("ofile:%s", fname.c_str());
5485             processDependency(ofile, include, 0);
5486             }
5487         }
5489       
5490     return true;
5495 /**
5496  *  High-level call to generate deps and optionally save them
5497  */
5498 bool DepTool::generateDependencies(const String &fileName)
5500     if (!createFileList())
5501         return false;
5502     if (!generateDependencies())
5503         return false;
5504     if (!saveDepFile(fileName))
5505         return false;
5506     return true;
5510 /**
5511  *   This saves the dependency cache.
5512  */
5513 bool DepTool::saveDepFile(const String &fileName)
5515     time_t tim;
5516     time(&tim);
5518     FILE *f = fopen(fileName.c_str(), "w");
5519     if (!f)
5520         {
5521         trace("cannot open '%s' for writing", fileName.c_str());
5522         }
5523     fprintf(f, "<?xml version='1.0'?>\n");
5524     fprintf(f, "<!--\n");
5525     fprintf(f, "########################################################\n");
5526     fprintf(f, "## File: build.dep\n");
5527     fprintf(f, "## Generated by BuildTool at :%s", ctime(&tim));
5528     fprintf(f, "########################################################\n");
5529     fprintf(f, "-->\n");
5531     fprintf(f, "<dependencies source='%s'>\n\n", sourceDir.c_str());
5532     std::map<String, FileRec *>::iterator iter;
5533     for (iter=depFiles.begin() ; iter!=depFiles.end() ; iter++)
5534         {
5535         FileRec *frec = iter->second;
5536         if (frec->type == FileRec::OFILE)
5537             {
5538             fprintf(f, "<object path='%s' name='%s' suffix='%s'>\n",
5539                  frec->path.c_str(), frec->baseName.c_str(), frec->suffix.c_str());
5540             std::map<String, FileRec *>::iterator citer;
5541             for (citer=frec->files.begin() ; citer!=frec->files.end() ; citer++)
5542                 {
5543                 String cfname = citer->first;
5544                 fprintf(f, "    <dep name='%s'/>\n", cfname.c_str());
5545                 }
5546             fprintf(f, "</object>\n\n");
5547             }
5548         }
5550     fprintf(f, "</dependencies>\n");
5551     fprintf(f, "\n");
5552     fprintf(f, "<!--\n");
5553     fprintf(f, "########################################################\n");
5554     fprintf(f, "## E N D\n");
5555     fprintf(f, "########################################################\n");
5556     fprintf(f, "-->\n");
5558     fclose(f);
5560     return true;
5566 /**
5567  *   This loads the dependency cache.
5568  */
5569 std::vector<DepRec> DepTool::loadDepFile(const String &depFile)
5571     std::vector<DepRec> result;
5572     
5573     Parser parser;
5574     Element *root = parser.parseFile(depFile.c_str());
5575     if (!root)
5576         {
5577         //error("Could not open %s for reading", depFile.c_str());
5578         return result;
5579         }
5581     if (root->getChildren().size()==0 ||
5582         root->getChildren()[0]->getName()!="dependencies")
5583         {
5584         error("Main xml element should be <dependencies>");
5585         delete root;
5586         return result;
5587         }
5589     //########## Start parsing
5590     Element *depList = root->getChildren()[0];
5592     std::vector<Element *> objects = depList->getChildren();
5593     for (unsigned int i=0 ; i<objects.size() ; i++)
5594         {
5595         Element *objectElem = objects[i];
5596         String tagName = objectElem->getName();
5597         if (tagName == "object")
5598             {
5599             String objName   = objectElem->getAttribute("name");
5600              //trace("object:%s", objName.c_str());
5601             DepRec depObject(objName);
5602             depObject.path   = objectElem->getAttribute("path");
5603             depObject.suffix = objectElem->getAttribute("suffix");
5604             //########## DESCRIPTION
5605             std::vector<Element *> depElems = objectElem->getChildren();
5606             for (unsigned int i=0 ; i<depElems.size() ; i++)
5607                 {
5608                 Element *depElem = depElems[i];
5609                 tagName = depElem->getName();
5610                 if (tagName == "dep")
5611                     {
5612                     String depName = depElem->getAttribute("name");
5613                     //trace("    dep:%s", depName.c_str());
5614                     depObject.files.push_back(depName);
5615                     }
5616                 }
5617             //Insert into the result list, in a sorted manner
5618             bool inserted = false;
5619             std::vector<DepRec>::iterator iter;
5620             for (iter = result.begin() ; iter != result.end() ; iter++)
5621                 {
5622                 String vpath = iter->path;
5623                 vpath.append("/");
5624                 vpath.append(iter->name);
5625                 String opath = depObject.path;
5626                 opath.append("/");
5627                 opath.append(depObject.name);
5628                 if (vpath > opath)
5629                     {
5630                     inserted = true;
5631                     iter = result.insert(iter, depObject);
5632                     break;
5633                     }
5634                 }
5635             if (!inserted)
5636                 result.push_back(depObject);
5637             }
5638         }
5640     delete root;
5642     return result;
5646 /**
5647  *   This loads the dependency cache.
5648  */
5649 std::vector<DepRec> DepTool::getDepFile(const String &depFile,
5650                    bool forceRefresh)
5652     std::vector<DepRec> result;
5653     if (forceRefresh)
5654         {
5655         generateDependencies(depFile);
5656         result = loadDepFile(depFile);
5657         }
5658     else
5659         {
5660         //try once
5661         result = loadDepFile(depFile);
5662         if (result.size() == 0)
5663             {
5664             //fail? try again
5665             generateDependencies(depFile);
5666             result = loadDepFile(depFile);
5667             }
5668         }
5669     return result;
5675 //########################################################################
5676 //# T A S K
5677 //########################################################################
5678 //forward decl
5679 class Target;
5680 class Make;
5682 /**
5683  *
5684  */
5685 class Task : public MakeBase
5688 public:
5690     typedef enum
5691         {
5692         TASK_NONE,
5693         TASK_CC,
5694         TASK_COPY,
5695         TASK_DELETE,
5696         TASK_JAR,
5697         TASK_JAVAC,
5698         TASK_LINK,
5699         TASK_MAKEFILE,
5700         TASK_MKDIR,
5701         TASK_MSGFMT,
5702         TASK_RANLIB,
5703         TASK_RC,
5704         TASK_SHAREDLIB,
5705         TASK_STATICLIB,
5706         TASK_STRIP,
5707         TASK_TOUCH,
5708         TASK_TSTAMP
5709         } TaskType;
5710         
5712     /**
5713      *
5714      */
5715     Task(MakeBase &par) : parent(par)
5716         { init(); }
5718     /**
5719      *
5720      */
5721     Task(const Task &other) : parent(other.parent)
5722         { init(); assign(other); }
5724     /**
5725      *
5726      */
5727     Task &operator=(const Task &other)
5728         { assign(other); return *this; }
5730     /**
5731      *
5732      */
5733     virtual ~Task()
5734         { }
5737     /**
5738      *
5739      */
5740     virtual MakeBase &getParent()
5741         { return parent; }
5743      /**
5744      *
5745      */
5746     virtual int  getType()
5747         { return type; }
5749     /**
5750      *
5751      */
5752     virtual void setType(int val)
5753         { type = val; }
5755     /**
5756      *
5757      */
5758     virtual String getName()
5759         { return name; }
5761     /**
5762      *
5763      */
5764     virtual bool execute()
5765         { return true; }
5767     /**
5768      *
5769      */
5770     virtual bool parse(Element *elem)
5771         { return true; }
5773     /**
5774      *
5775      */
5776     Task *createTask(Element *elem, int lineNr);
5779 protected:
5781     void init()
5782         {
5783         type = TASK_NONE;
5784         name = "none";
5785         }
5787     void assign(const Task &other)
5788         {
5789         type = other.type;
5790         name = other.name;
5791         }
5792         
5793     String getAttribute(Element *elem, const String &attrName)
5794         {
5795         String str;
5796         return str;
5797         }
5799     MakeBase &parent;
5801     int type;
5803     String name;
5804 };
5808 /**
5809  * This task runs the C/C++ compiler.  The compiler is invoked
5810  * for all .c or .cpp files which are newer than their correcsponding
5811  * .o files.  
5812  */
5813 class TaskCC : public Task
5815 public:
5817     TaskCC(MakeBase &par) : Task(par)
5818         {
5819         type = TASK_CC; name = "cc";
5820         ccCommand   = "gcc";
5821         cxxCommand  = "g++";
5822         source      = ".";
5823         dest        = ".";
5824         flags       = "";
5825         defines     = "";
5826         includes    = "";
5827         fileSet.clear();
5828         }
5830     virtual ~TaskCC()
5831         {}
5833     virtual bool needsCompiling(const DepRec &depRec,
5834               const String &src, const String &dest)
5835         {
5836         return false;
5837         }
5839     virtual bool execute()
5840         {
5841         if (!listFiles(parent, fileSet))
5842             return false;
5843             
5844         FILE *f = NULL;
5845         f = fopen("compile.lst", "w");
5847         bool refreshCache = false;
5848         String fullName = parent.resolve("build.dep");
5849         if (isNewerThan(parent.getURI().getPath(), fullName))
5850             {
5851             status("          : regenerating C/C++ dependency cache");
5852             refreshCache = true;
5853             }
5855         DepTool depTool;
5856         depTool.setSourceDirectory(source);
5857         depTool.setFileList(fileSet.getFiles());
5858         std::vector<DepRec> deps =
5859              depTool.getDepFile("build.dep", refreshCache);
5860         
5861         String incs;
5862         incs.append("-I");
5863         incs.append(parent.resolve("."));
5864         incs.append(" ");
5865         if (includes.size()>0)
5866             {
5867             incs.append(includes);
5868             incs.append(" ");
5869             }
5870         std::set<String> paths;
5871         std::vector<DepRec>::iterator viter;
5872         for (viter=deps.begin() ; viter!=deps.end() ; viter++)
5873             {
5874             DepRec dep = *viter;
5875             if (dep.path.size()>0)
5876                 paths.insert(dep.path);
5877             }
5878         if (source.size()>0)
5879             {
5880             incs.append(" -I");
5881             incs.append(parent.resolve(source));
5882             incs.append(" ");
5883             }
5884         std::set<String>::iterator setIter;
5885         for (setIter=paths.begin() ; setIter!=paths.end() ; setIter++)
5886             {
5887             incs.append(" -I");
5888             String dname;
5889             if (source.size()>0)
5890                 {
5891                 dname.append(source);
5892                 dname.append("/");
5893                 }
5894             dname.append(*setIter);
5895             incs.append(parent.resolve(dname));
5896             }
5897         std::vector<String> cfiles;
5898         for (viter=deps.begin() ; viter!=deps.end() ; viter++)
5899             {
5900             DepRec dep = *viter;
5902             //## Select command
5903             String sfx = dep.suffix;
5904             String command = ccCommand;
5905             if (sfx == "cpp" || sfx == "c++" || sfx == "cc"
5906                  || sfx == "CC")
5907                 command = cxxCommand;
5908  
5909             //## Make paths
5910             String destPath = dest;
5911             String srcPath  = source;
5912             if (dep.path.size()>0)
5913                 {
5914                 destPath.append("/");
5915                 destPath.append(dep.path);
5916                 srcPath.append("/");
5917                 srcPath.append(dep.path);
5918                 }
5919             //## Make sure destination directory exists
5920             if (!createDirectory(destPath))
5921                 return false;
5922                 
5923             //## Check whether it needs to be done
5924             String destName;
5925             if (destPath.size()>0)
5926                 {
5927                 destName.append(destPath);
5928                 destName.append("/");
5929                 }
5930             destName.append(dep.name);
5931             destName.append(".o");
5932             String destFullName = parent.resolve(destName);
5933             String srcName;
5934             if (srcPath.size()>0)
5935                 {
5936                 srcName.append(srcPath);
5937                 srcName.append("/");
5938                 }
5939             srcName.append(dep.name);
5940             srcName.append(".");
5941             srcName.append(dep.suffix);
5942             String srcFullName = parent.resolve(srcName);
5943             bool compileMe = false;
5944             if (isNewerThan(srcFullName, destFullName))
5945                 {
5946                 status("          : compile of %s required by %s",
5947                         destFullName.c_str(), srcFullName.c_str());
5948                 compileMe = true;
5949                 }
5950             else
5951                 {
5952                 for (unsigned int i=0 ; i<dep.files.size() ; i++)
5953                     {
5954                     String depName;
5955                     if (srcPath.size()>0)
5956                         {
5957                         depName.append(srcPath);
5958                         depName.append("/");
5959                         }
5960                     depName.append(dep.files[i]);
5961                     String depFullName = parent.resolve(depName);
5962                     if (isNewerThan(depFullName, destFullName))
5963                         {
5964                         status("          : compile of %s required by %s",
5965                                 destFullName.c_str(), depFullName.c_str());
5966                         compileMe = true;
5967                         break;
5968                         }
5969                     }
5970                 }
5971             if (!compileMe)
5972                 {
5973                 continue;
5974                 }
5976             //## Assemble the command
5977             String cmd = command;
5978             cmd.append(" -c ");
5979             cmd.append(flags);
5980             cmd.append(" ");
5981             cmd.append(defines);
5982             cmd.append(" ");
5983             cmd.append(incs);
5984             cmd.append(" ");
5985             cmd.append(srcFullName);
5986             cmd.append(" -o ");
5987             cmd.append(destFullName);
5989             //## Execute the command
5991             String outString, errString;
5992             bool ret = executeCommand(cmd.c_str(), "", outString, errString);
5994             if (f)
5995                 {
5996                 fprintf(f, "########################### File : %s\n",
5997                              srcFullName.c_str());
5998                 fprintf(f, "#### COMMAND ###\n");
5999                 int col = 0;
6000                 for (int i = 0 ; i < cmd.size() ; i++)
6001                     {
6002                     char ch = cmd[i];
6003                     if (isspace(ch)  && col > 63)
6004                         {
6005                         fputc('\n', f);
6006                         col = 0;
6007                         }
6008                     else
6009                         {
6010                         fputc(ch, f);
6011                         col++;
6012                         }
6013                     if (col > 76)
6014                         {
6015                         fputc('\n', f);
6016                         col = 0;
6017                         }
6018                     }
6019                 fprintf(f, "\n");
6020                 fprintf(f, "#### STDOUT ###\n%s\n", outString.c_str());
6021                 fprintf(f, "#### STDERR ###\n%s\n\n", errString.c_str());
6022                 }
6023             if (!ret)
6024                 {
6025                 error("problem compiling: %s", errString.c_str());
6026                 return false;
6027                 }
6028                 
6029             }
6031         if (f)
6032             {
6033             fclose(f);
6034             }
6035         
6036         return true;
6037         }
6039     virtual bool parse(Element *elem)
6040         {
6041         String s;
6042         if (!parent.getAttribute(elem, "command", s))
6043             return false;
6044         if (s.size()>0) { ccCommand = s; cxxCommand = s; }
6045         if (!parent.getAttribute(elem, "cc", s))
6046             return false;
6047         if (s.size()>0) ccCommand = s;
6048         if (!parent.getAttribute(elem, "cxx", s))
6049             return false;
6050         if (s.size()>0) cxxCommand = s;
6051         if (!parent.getAttribute(elem, "destdir", s))
6052             return false;
6053         if (s.size()>0) dest = s;
6055         std::vector<Element *> children = elem->getChildren();
6056         for (unsigned int i=0 ; i<children.size() ; i++)
6057             {
6058             Element *child = children[i];
6059             String tagName = child->getName();
6060             if (tagName == "flags")
6061                 {
6062                 if (!parent.getValue(child, flags))
6063                     return false;
6064                 flags = strip(flags);
6065                 }
6066             else if (tagName == "includes")
6067                 {
6068                 if (!parent.getValue(child, includes))
6069                     return false;
6070                 includes = strip(includes);
6071                 }
6072             else if (tagName == "defines")
6073                 {
6074                 if (!parent.getValue(child, defines))
6075                     return false;
6076                 defines = strip(defines);
6077                 }
6078             else if (tagName == "fileset")
6079                 {
6080                 if (!parseFileSet(child, parent, fileSet))
6081                     return false;
6082                 source = fileSet.getDirectory();
6083                 }
6084             }
6086         return true;
6087         }
6088         
6089 protected:
6091     String ccCommand;
6092     String cxxCommand;
6093     String source;
6094     String dest;
6095     String flags;
6096     String defines;
6097     String includes;
6098     FileSet fileSet;
6099     
6100 };
6104 /**
6105  *
6106  */
6107 class TaskCopy : public Task
6109 public:
6111     typedef enum
6112         {
6113         CP_NONE,
6114         CP_TOFILE,
6115         CP_TODIR
6116         } CopyType;
6118     TaskCopy(MakeBase &par) : Task(par)
6119         {
6120         type = TASK_COPY; name = "copy";
6121         cptype = CP_NONE;
6122         verbose = false;
6123         haveFileSet = false;
6124         }
6126     virtual ~TaskCopy()
6127         {}
6129     virtual bool execute()
6130         {
6131         switch (cptype)
6132            {
6133            case CP_TOFILE:
6134                {
6135                if (fileName.size()>0)
6136                    {
6137                    status("          : %s to %s",
6138                         fileName.c_str(), toFileName.c_str());
6139                    String fullSource = parent.resolve(fileName);
6140                    String fullDest = parent.resolve(toFileName);
6141                    //trace("copy %s to file %s", fullSource.c_str(),
6142                    //                       fullDest.c_str());
6143                    if (!isRegularFile(fullSource))
6144                        {
6145                        error("copy : file %s does not exist", fullSource.c_str());
6146                        return false;
6147                        }
6148                    if (!isNewerThan(fullSource, fullDest))
6149                        {
6150                        return true;
6151                        }
6152                    if (!copyFile(fullSource, fullDest))
6153                        return false;
6154                    status("          : 1 file copied");
6155                    }
6156                return true;
6157                }
6158            case CP_TODIR:
6159                {
6160                if (haveFileSet)
6161                    {
6162                    if (!listFiles(parent, fileSet))
6163                        return false;
6164                    String fileSetDir = fileSet.getDirectory();
6166                    status("          : %s to %s",
6167                        fileSetDir.c_str(), toDirName.c_str());
6169                    int nrFiles = 0;
6170                    for (unsigned int i=0 ; i<fileSet.size() ; i++)
6171                        {
6172                        String fileName = fileSet[i];
6174                        String sourcePath;
6175                        if (fileSetDir.size()>0)
6176                            {
6177                            sourcePath.append(fileSetDir);
6178                            sourcePath.append("/");
6179                            }
6180                        sourcePath.append(fileName);
6181                        String fullSource = parent.resolve(sourcePath);
6182                        
6183                        //Get the immediate parent directory's base name
6184                        String baseFileSetDir = fileSetDir;
6185                        unsigned int pos = baseFileSetDir.find_last_of('/');
6186                        if (pos!=baseFileSetDir.npos &&
6187                                   pos < baseFileSetDir.size()-1)
6188                            baseFileSetDir =
6189                               baseFileSetDir.substr(pos+1,
6190                                    baseFileSetDir.size());
6191                        //Now make the new path
6192                        String destPath;
6193                        if (toDirName.size()>0)
6194                            {
6195                            destPath.append(toDirName);
6196                            destPath.append("/");
6197                            }
6198                        if (baseFileSetDir.size()>0)
6199                            {
6200                            destPath.append(baseFileSetDir);
6201                            destPath.append("/");
6202                            }
6203                        destPath.append(fileName);
6204                        String fullDest = parent.resolve(destPath);
6205                        //trace("fileName:%s", fileName.c_str());
6206                        //trace("copy %s to new dir : %s", fullSource.c_str(),
6207                        //                   fullDest.c_str());
6208                        if (!isNewerThan(fullSource, fullDest))
6209                            {
6210                            //trace("copy skipping %s", fullSource.c_str());
6211                            continue;
6212                            }
6213                        if (!copyFile(fullSource, fullDest))
6214                            return false;
6215                        nrFiles++;
6216                        }
6217                    status("          : %d file(s) copied", nrFiles);
6218                    }
6219                else //file source
6220                    {
6221                    //For file->dir we want only the basename of
6222                    //the source appended to the dest dir
6223                    status("          : %s to %s", 
6224                        fileName.c_str(), toDirName.c_str());
6225                    String baseName = fileName;
6226                    unsigned int pos = baseName.find_last_of('/');
6227                    if (pos!=baseName.npos && pos<baseName.size()-1)
6228                        baseName = baseName.substr(pos+1, baseName.size());
6229                    String fullSource = parent.resolve(fileName);
6230                    String destPath;
6231                    if (toDirName.size()>0)
6232                        {
6233                        destPath.append(toDirName);
6234                        destPath.append("/");
6235                        }
6236                    destPath.append(baseName);
6237                    String fullDest = parent.resolve(destPath);
6238                    //trace("copy %s to new dir : %s", fullSource.c_str(),
6239                    //                       fullDest.c_str());
6240                    if (!isRegularFile(fullSource))
6241                        {
6242                        error("copy : file %s does not exist", fullSource.c_str());
6243                        return false;
6244                        }
6245                    if (!isNewerThan(fullSource, fullDest))
6246                        {
6247                        return true;
6248                        }
6249                    if (!copyFile(fullSource, fullDest))
6250                        return false;
6251                    status("          : 1 file copied");
6252                    }
6253                return true;
6254                }
6255            }
6256         return true;
6257         }
6260     virtual bool parse(Element *elem)
6261         {
6262         if (!parent.getAttribute(elem, "file", fileName))
6263             return false;
6264         if (!parent.getAttribute(elem, "tofile", toFileName))
6265             return false;
6266         if (toFileName.size() > 0)
6267             cptype = CP_TOFILE;
6268         if (!parent.getAttribute(elem, "todir", toDirName))
6269             return false;
6270         if (toDirName.size() > 0)
6271             cptype = CP_TODIR;
6272         String ret;
6273         if (!parent.getAttribute(elem, "verbose", ret))
6274             return false;
6275         if (ret.size()>0 && !getBool(ret, verbose))
6276             return false;
6277             
6278         haveFileSet = false;
6279         
6280         std::vector<Element *> children = elem->getChildren();
6281         for (unsigned int i=0 ; i<children.size() ; i++)
6282             {
6283             Element *child = children[i];
6284             String tagName = child->getName();
6285             if (tagName == "fileset")
6286                 {
6287                 if (!parseFileSet(child, parent, fileSet))
6288                     {
6289                     error("problem getting fileset");
6290                     return false;
6291                     }
6292                 haveFileSet = true;
6293                 }
6294             }
6296         //Perform validity checks
6297         if (fileName.size()>0 && fileSet.size()>0)
6298             {
6299             error("<copy> can only have one of : file= and <fileset>");
6300             return false;
6301             }
6302         if (toFileName.size()>0 && toDirName.size()>0)
6303             {
6304             error("<copy> can only have one of : tofile= or todir=");
6305             return false;
6306             }
6307         if (haveFileSet && toDirName.size()==0)
6308             {
6309             error("a <copy> task with a <fileset> must have : todir=");
6310             return false;
6311             }
6312         if (cptype == CP_TOFILE && fileName.size()==0)
6313             {
6314             error("<copy> tofile= must be associated with : file=");
6315             return false;
6316             }
6317         if (cptype == CP_TODIR && fileName.size()==0 && !haveFileSet)
6318             {
6319             error("<copy> todir= must be associated with : file= or <fileset>");
6320             return false;
6321             }
6323         return true;
6324         }
6325         
6326 private:
6328     int cptype;
6329     String fileName;
6330     FileSet fileSet;
6331     String toFileName;
6332     String toDirName;
6333     bool verbose;
6334     bool haveFileSet;
6335 };
6338 /**
6339  *
6340  */
6341 class TaskDelete : public Task
6343 public:
6345     typedef enum
6346         {
6347         DEL_FILE,
6348         DEL_DIR,
6349         DEL_FILESET
6350         } DeleteType;
6352     TaskDelete(MakeBase &par) : Task(par)
6353         { 
6354           type        = TASK_DELETE;
6355           name        = "delete";
6356           delType     = DEL_FILE;
6357           verbose     = false;
6358           quiet       = false;
6359           failOnError = true;
6360         }
6362     virtual ~TaskDelete()
6363         {}
6365     virtual bool execute()
6366         {
6367         struct stat finfo;
6368         switch (delType)
6369             {
6370             case DEL_FILE:
6371                 {
6372                 status("          : %s", fileName.c_str());
6373                 String fullName = parent.resolve(fileName);
6374                 char *fname = (char *)fullName.c_str();
6375                 //does not exist
6376                 if (stat(fname, &finfo)<0)
6377                     return true;
6378                 //exists but is not a regular file
6379                 if (!S_ISREG(finfo.st_mode))
6380                     {
6381                     error("<delete> failed. '%s' exists and is not a regular file",
6382                           fname);
6383                     return false;
6384                     }
6385                 if (remove(fname)<0)
6386                     {
6387                     error("<delete> failed: %s", strerror(errno));
6388                     return false;
6389                     }
6390                 return true;
6391                 }
6392             case DEL_DIR:
6393                 {
6394                 status("          : %s", dirName.c_str());
6395                 String fullDir = parent.resolve(dirName);
6396                 if (!removeDirectory(fullDir))
6397                     return false;
6398                 return true;
6399                 }
6400             }
6401         return true;
6402         }
6404     virtual bool parse(Element *elem)
6405         {
6406         if (!parent.getAttribute(elem, "file", fileName))
6407             return false;
6408         if (fileName.size() > 0)
6409             delType = DEL_FILE;
6410         if (!parent.getAttribute(elem, "dir", dirName))
6411             return false;
6412         if (dirName.size() > 0)
6413             delType = DEL_DIR;
6414         if (fileName.size()>0 && dirName.size()>0)
6415             {
6416             error("<delete> can have one attribute of file= or dir=");
6417             return false;
6418             }
6419         if (fileName.size()==0 && dirName.size()==0)
6420             {
6421             error("<delete> must have one attribute of file= or dir=");
6422             return false;
6423             }
6424         String ret;
6425         if (!parent.getAttribute(elem, "verbose", ret))
6426             return false;
6427         if (ret.size()>0 && !getBool(ret, verbose))
6428             return false;
6429         if (!parent.getAttribute(elem, "quiet", ret))
6430             return false;
6431         if (ret.size()>0 && !getBool(ret, quiet))
6432             return false;
6433         if (!parent.getAttribute(elem, "failonerror", ret))
6434             return false;
6435         if (ret.size()>0 && !getBool(ret, failOnError))
6436             return false;
6437         return true;
6438         }
6440 private:
6442     int delType;
6443     String dirName;
6444     String fileName;
6445     bool verbose;
6446     bool quiet;
6447     bool failOnError;
6448 };
6451 /**
6452  *
6453  */
6454 class TaskJar : public Task
6456 public:
6458     TaskJar(MakeBase &par) : Task(par)
6459         { type = TASK_JAR; name = "jar"; }
6461     virtual ~TaskJar()
6462         {}
6464     virtual bool execute()
6465         {
6466         return true;
6467         }
6469     virtual bool parse(Element *elem)
6470         {
6471         return true;
6472         }
6473 };
6476 /**
6477  *
6478  */
6479 class TaskJavac : public Task
6481 public:
6483     TaskJavac(MakeBase &par) : Task(par)
6484         { type = TASK_JAVAC; name = "javac"; }
6486     virtual ~TaskJavac()
6487         {}
6489     virtual bool execute()
6490         {
6491         return true;
6492         }
6494     virtual bool parse(Element *elem)
6495         {
6496         return true;
6497         }
6498 };
6501 /**
6502  *
6503  */
6504 class TaskLink : public Task
6506 public:
6508     TaskLink(MakeBase &par) : Task(par)
6509         {
6510         type = TASK_LINK; name = "link";
6511         command = "g++";
6512         doStrip = false;
6513                 stripCommand = "strip";
6514                 objcopyCommand = "objcopy";
6515         }
6517     virtual ~TaskLink()
6518         {}
6520     virtual bool execute()
6521         {
6522         if (!listFiles(parent, fileSet))
6523             return false;
6524         String fileSetDir = fileSet.getDirectory();
6525         //trace("%d files in %s", fileSet.size(), fileSetDir.c_str());
6526         bool doit = false;
6527         String fullTarget = parent.resolve(fileName);
6528         String cmd = command;
6529         cmd.append(" -o ");
6530         cmd.append(fullTarget);
6531         cmd.append(" ");
6532         cmd.append(flags);
6533         for (unsigned int i=0 ; i<fileSet.size() ; i++)
6534             {
6535             cmd.append(" ");
6536             String obj;
6537             if (fileSetDir.size()>0)
6538                 {
6539                 obj.append(fileSetDir);
6540                 obj.append("/");
6541                 }
6542             obj.append(fileSet[i]);
6543             String fullObj = parent.resolve(obj);
6544             cmd.append(fullObj);
6545             //trace("link: tgt:%s obj:%s", fullTarget.c_str(),
6546             //          fullObj.c_str());
6547             if (isNewerThan(fullObj, fullTarget))
6548                 doit = true;
6549             }
6550         cmd.append(" ");
6551         cmd.append(libs);
6552         if (!doit)
6553             {
6554             //trace("link not needed");
6555             return true;
6556             }
6557         //trace("LINK cmd:%s", cmd.c_str());
6560         String outbuf, errbuf;
6561         if (!executeCommand(cmd.c_str(), "", outbuf, errbuf))
6562             {
6563             error("LINK problem: %s", errbuf.c_str());
6564             return false;
6565             }
6567         if (symFileName.size()>0)
6568             {
6569             String symFullName = parent.resolve(symFileName);
6570             cmd = objcopyCommand;
6571             cmd.append(" --only-keep-debug ");
6572             cmd.append(getNativePath(fullTarget));
6573             cmd.append(" ");
6574             cmd.append(getNativePath(symFullName));
6575             if (!executeCommand(cmd, "", outbuf, errbuf))
6576                 {
6577                 error("<strip> symbol file failed : %s", errbuf.c_str());
6578                 return false;
6579                 }
6580             }
6581             
6582         if (doStrip)
6583             {
6584             cmd = stripCommand;
6585             cmd.append(" ");
6586             cmd.append(getNativePath(fullTarget));
6587             if (!executeCommand(cmd, "", outbuf, errbuf))
6588                {
6589                error("<strip> failed : %s", errbuf.c_str());
6590                return false;
6591                }
6592             }
6594         return true;
6595         }
6597     virtual bool parse(Element *elem)
6598         {
6599         String s;
6600         if (!parent.getAttribute(elem, "command", s))
6601             return false;
6602         if (s.size()>0)
6603             command = s;
6604         if (!parent.getAttribute(elem, "objcopycommand", s))
6605             return false;
6606         if (s.size()>0)
6607             objcopyCommand = s;
6608         if (!parent.getAttribute(elem, "stripcommand", s))
6609             return false;
6610         if (s.size()>0)
6611             stripCommand = s;
6612         if (!parent.getAttribute(elem, "out", fileName))
6613             return false;
6614         if (!parent.getAttribute(elem, "strip", s))
6615             return false;
6616         if (s.size()>0 && !getBool(s, doStrip))
6617             return false;
6618         if (!parent.getAttribute(elem, "symfile", symFileName))
6619             return false;
6620             
6621         std::vector<Element *> children = elem->getChildren();
6622         for (unsigned int i=0 ; i<children.size() ; i++)
6623             {
6624             Element *child = children[i];
6625             String tagName = child->getName();
6626             if (tagName == "fileset")
6627                 {
6628                 if (!parseFileSet(child, parent, fileSet))
6629                     return false;
6630                 }
6631             else if (tagName == "flags")
6632                 {
6633                 if (!parent.getValue(child, flags))
6634                     return false;
6635                 flags = strip(flags);
6636                 }
6637             else if (tagName == "libs")
6638                 {
6639                 if (!parent.getValue(child, libs))
6640                     return false;
6641                 libs = strip(libs);
6642                 }
6643             }
6644         return true;
6645         }
6647 private:
6649     String  command;
6650     String  fileName;
6651     String  flags;
6652     String  libs;
6653     FileSet fileSet;
6654     bool    doStrip;
6655     String  symFileName;
6656     String  stripCommand;
6657     String  objcopyCommand;
6659 };
6663 /**
6664  * Create a named directory
6665  */
6666 class TaskMakeFile : public Task
6668 public:
6670     TaskMakeFile(MakeBase &par) : Task(par)
6671         { type = TASK_MAKEFILE; name = "makefile"; }
6673     virtual ~TaskMakeFile()
6674         {}
6676     virtual bool execute()
6677         {
6678         status("          : %s", fileName.c_str());
6679         String fullName = parent.resolve(fileName);
6680         if (!isNewerThan(parent.getURI().getPath(), fullName))
6681             {
6682             //trace("skipped <makefile>");
6683             return true;
6684             }
6685         //trace("fullName:%s", fullName.c_str());
6686         FILE *f = fopen(fullName.c_str(), "w");
6687         if (!f)
6688             {
6689             error("<makefile> could not open %s for writing : %s",
6690                 fullName.c_str(), strerror(errno));
6691             return false;
6692             }
6693         for (unsigned int i=0 ; i<text.size() ; i++)
6694             fputc(text[i], f);
6695         fputc('\n', f);
6696         fclose(f);
6697         return true;
6698         }
6700     virtual bool parse(Element *elem)
6701         {
6702         if (!parent.getAttribute(elem, "file", fileName))
6703             return false;
6704         if (fileName.size() == 0)
6705             {
6706             error("<makefile> requires 'file=\"filename\"' attribute");
6707             return false;
6708             }
6709         if (!parent.getValue(elem, text))
6710             return false;
6711         text = leftJustify(text);
6712         //trace("dirname:%s", dirName.c_str());
6713         return true;
6714         }
6716 private:
6718     String fileName;
6719     String text;
6720 };
6724 /**
6725  * Create a named directory
6726  */
6727 class TaskMkDir : public Task
6729 public:
6731     TaskMkDir(MakeBase &par) : Task(par)
6732         { type = TASK_MKDIR; name = "mkdir"; }
6734     virtual ~TaskMkDir()
6735         {}
6737     virtual bool execute()
6738         {
6739         status("          : %s", dirName.c_str());
6740         String fullDir = parent.resolve(dirName);
6741         //trace("fullDir:%s", fullDir.c_str());
6742         if (!createDirectory(fullDir))
6743             return false;
6744         return true;
6745         }
6747     virtual bool parse(Element *elem)
6748         {
6749         if (!parent.getAttribute(elem, "dir", dirName))
6750             return false;
6751         if (dirName.size() == 0)
6752             {
6753             error("<mkdir> requires 'dir=\"dirname\"' attribute");
6754             return false;
6755             }
6756         return true;
6757         }
6759 private:
6761     String dirName;
6762 };
6766 /**
6767  * Create a named directory
6768  */
6769 class TaskMsgFmt: public Task
6771 public:
6773     TaskMsgFmt(MakeBase &par) : Task(par)
6774          {
6775          type    = TASK_MSGFMT;
6776          name    = "msgfmt";
6777          command = "msgfmt";
6778          owndir  = false;
6779          outName = "";
6780          }
6782     virtual ~TaskMsgFmt()
6783         {}
6785     virtual bool execute()
6786         {
6787         if (!listFiles(parent, fileSet))
6788             return false;
6789         String fileSetDir = fileSet.getDirectory();
6791         //trace("msgfmt: %d", fileSet.size());
6792         for (unsigned int i=0 ; i<fileSet.size() ; i++)
6793             {
6794             String fileName = fileSet[i];
6795             if (getSuffix(fileName) != "po")
6796                 continue;
6797             String sourcePath;
6798             if (fileSetDir.size()>0)
6799                 {
6800                 sourcePath.append(fileSetDir);
6801                 sourcePath.append("/");
6802                 }
6803             sourcePath.append(fileName);
6804             String fullSource = parent.resolve(sourcePath);
6806             String destPath;
6807             if (toDirName.size()>0)
6808                 {
6809                 destPath.append(toDirName);
6810                 destPath.append("/");
6811                 }
6812             if (owndir)
6813                 {
6814                 String subdir = fileName;
6815                 unsigned int pos = subdir.find_last_of('.');
6816                 if (pos != subdir.npos)
6817                     subdir = subdir.substr(0, pos);
6818                 destPath.append(subdir);
6819                 destPath.append("/");
6820                 }
6821             //Pick the output file name
6822             if (outName.size() > 0)
6823                 {
6824                 destPath.append(outName);
6825                 }
6826             else
6827                 {
6828                 destPath.append(fileName);
6829                 destPath[destPath.size()-2] = 'm';
6830                 }
6832             String fullDest = parent.resolve(destPath);
6834             if (!isNewerThan(fullSource, fullDest))
6835                 {
6836                 //trace("skip %s", fullSource.c_str());
6837                 continue;
6838                 }
6839                 
6840             String cmd = command;
6841             cmd.append(" ");
6842             cmd.append(fullSource);
6843             cmd.append(" -o ");
6844             cmd.append(fullDest);
6845             
6846             int pos = fullDest.find_last_of('/');
6847             if (pos>0)
6848                 {
6849                 String fullDestPath = fullDest.substr(0, pos);
6850                 if (!createDirectory(fullDestPath))
6851                     return false;
6852                 }
6856             String outString, errString;
6857             if (!executeCommand(cmd.c_str(), "", outString, errString))
6858                 {
6859                 error("<msgfmt> problem: %s", errString.c_str());
6860                 return false;
6861                 }
6862             }
6864         return true;
6865         }
6867     virtual bool parse(Element *elem)
6868         {
6869         String s;
6870         if (!parent.getAttribute(elem, "command", s))
6871             return false;
6872         if (s.size()>0)
6873             command = s;
6874         if (!parent.getAttribute(elem, "todir", toDirName))
6875             return false;
6876         if (!parent.getAttribute(elem, "out", outName))
6877             return false;
6878         if (!parent.getAttribute(elem, "owndir", s))
6879             return false;
6880         if (s.size()>0 && !getBool(s, owndir))
6881             return false;
6882             
6883         std::vector<Element *> children = elem->getChildren();
6884         for (unsigned int i=0 ; i<children.size() ; i++)
6885             {
6886             Element *child = children[i];
6887             String tagName = child->getName();
6888             if (tagName == "fileset")
6889                 {
6890                 if (!parseFileSet(child, parent, fileSet))
6891                     return false;
6892                 }
6893             }
6894         return true;
6895         }
6897 private:
6899     String  command;
6900     String  toDirName;
6901     String  outName;
6902     FileSet fileSet;
6903     bool    owndir;
6905 };
6911 /**
6912  *  Process an archive to allow random access
6913  */
6914 class TaskRanlib : public Task
6916 public:
6918     TaskRanlib(MakeBase &par) : Task(par)
6919         {
6920         type = TASK_RANLIB; name = "ranlib";
6921         command = "ranlib";
6922         }
6924     virtual ~TaskRanlib()
6925         {}
6927     virtual bool execute()
6928         {
6929         String fullName = parent.resolve(fileName);
6930         //trace("fullDir:%s", fullDir.c_str());
6931         String cmd = command;
6932         cmd.append(" ");
6933         cmd.append(fullName);
6934         String outbuf, errbuf;
6935         if (!executeCommand(cmd, "", outbuf, errbuf))
6936             return false;
6937         return true;
6938         }
6940     virtual bool parse(Element *elem)
6941         {
6942         String s;
6943         if (!parent.getAttribute(elem, "command", s))
6944             return false;
6945         if (s.size()>0)
6946            command = s;
6947         if (!parent.getAttribute(elem, "file", fileName))
6948             return false;
6949         if (fileName.size() == 0)
6950             {
6951             error("<ranlib> requires 'file=\"fileNname\"' attribute");
6952             return false;
6953             }
6954         return true;
6955         }
6957 private:
6959     String fileName;
6960     String command;
6961 };
6965 /**
6966  * Run the "ar" command to archive .o's into a .a
6967  */
6968 class TaskRC : public Task
6970 public:
6972     TaskRC(MakeBase &par) : Task(par)
6973         {
6974         type = TASK_RC; name = "rc";
6975         command = "windres";
6976         }
6978     virtual ~TaskRC()
6979         {}
6981     virtual bool execute()
6982         {
6983         String fullFile = parent.resolve(fileName);
6984         String fullOut  = parent.resolve(outName);
6985         if (!isNewerThan(fullFile, fullOut))
6986             return true;
6987         String cmd = command;
6988         cmd.append(" -o ");
6989         cmd.append(fullOut);
6990         cmd.append(" ");
6991         cmd.append(flags);
6992         cmd.append(" ");
6993         cmd.append(fullFile);
6995         String outString, errString;
6996         if (!executeCommand(cmd.c_str(), "", outString, errString))
6997             {
6998             error("RC problem: %s", errString.c_str());
6999             return false;
7000             }
7001         return true;
7002         }
7004     virtual bool parse(Element *elem)
7005         {
7006         if (!parent.getAttribute(elem, "command", command))
7007             return false;
7008         if (!parent.getAttribute(elem, "file", fileName))
7009             return false;
7010         if (!parent.getAttribute(elem, "out", outName))
7011             return false;
7012         std::vector<Element *> children = elem->getChildren();
7013         for (unsigned int i=0 ; i<children.size() ; i++)
7014             {
7015             Element *child = children[i];
7016             String tagName = child->getName();
7017             if (tagName == "flags")
7018                 {
7019                 if (!parent.getValue(child, flags))
7020                     return false;
7021                 }
7022             }
7023         return true;
7024         }
7026 private:
7028     String command;
7029     String flags;
7030     String fileName;
7031     String outName;
7033 };
7037 /**
7038  *  Collect .o's into a .so or DLL
7039  */
7040 class TaskSharedLib : public Task
7042 public:
7044     TaskSharedLib(MakeBase &par) : Task(par)
7045         {
7046         type = TASK_SHAREDLIB; name = "dll";
7047         command = "ar crv";
7048         }
7050     virtual ~TaskSharedLib()
7051         {}
7053     virtual bool execute()
7054         {
7055         //trace("###########HERE %d", fileSet.size());
7056         bool doit = false;
7057         
7058         String fullOut = parent.resolve(fileName);
7059         //trace("ar fullout: %s", fullOut.c_str());
7060         
7061         if (!listFiles(parent, fileSet))
7062             return false;
7063         String fileSetDir = fileSet.getDirectory();
7065         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7066             {
7067             String fname;
7068             if (fileSetDir.size()>0)
7069                 {
7070                 fname.append(fileSetDir);
7071                 fname.append("/");
7072                 }
7073             fname.append(fileSet[i]);
7074             String fullName = parent.resolve(fname);
7075             //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
7076             if (isNewerThan(fullName, fullOut))
7077                 doit = true;
7078             }
7079         //trace("Needs it:%d", doit);
7080         if (!doit)
7081             {
7082             return true;
7083             }
7085         String cmd = "dllwrap";
7086         cmd.append(" -o ");
7087         cmd.append(fullOut);
7088         if (defFileName.size()>0)
7089             {
7090             cmd.append(" --def ");
7091             cmd.append(defFileName);
7092             cmd.append(" ");
7093             }
7094         if (impFileName.size()>0)
7095             {
7096             cmd.append(" --implib ");
7097             cmd.append(impFileName);
7098             cmd.append(" ");
7099             }
7100         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7101             {
7102             String fname;
7103             if (fileSetDir.size()>0)
7104                 {
7105                 fname.append(fileSetDir);
7106                 fname.append("/");
7107                 }
7108             fname.append(fileSet[i]);
7109             String fullName = parent.resolve(fname);
7111             cmd.append(" ");
7112             cmd.append(fullName);
7113             }
7114         cmd.append(" ");
7115         cmd.append(libs);
7117         String outString, errString;
7118         if (!executeCommand(cmd.c_str(), "", outString, errString))
7119             {
7120             error("<sharedlib> problem: %s", errString.c_str());
7121             return false;
7122             }
7124         return true;
7125         }
7127     virtual bool parse(Element *elem)
7128         {
7129         if (!parent.getAttribute(elem, "file", fileName))
7130             return false;
7131         if (!parent.getAttribute(elem, "import", impFileName))
7132             return false;
7133         if (!parent.getAttribute(elem, "def", defFileName))
7134             return false;
7135             
7136         std::vector<Element *> children = elem->getChildren();
7137         for (unsigned int i=0 ; i<children.size() ; i++)
7138             {
7139             Element *child = children[i];
7140             String tagName = child->getName();
7141             if (tagName == "fileset")
7142                 {
7143                 if (!parseFileSet(child, parent, fileSet))
7144                     return false;
7145                 }
7146             else if (tagName == "libs")
7147                 {
7148                 if (!parent.getValue(child, libs))
7149                     return false;
7150                 libs = strip(libs);
7151                 }
7152             }
7153         return true;
7154         }
7156 private:
7158     String command;
7159     String fileName;
7160     String defFileName;
7161     String impFileName;
7162     FileSet fileSet;
7163     String libs;
7165 };
7169 /**
7170  * Run the "ar" command to archive .o's into a .a
7171  */
7172 class TaskStaticLib : public Task
7174 public:
7176     TaskStaticLib(MakeBase &par) : Task(par)
7177         {
7178         type = TASK_STATICLIB; name = "staticlib";
7179         command = "ar crv";
7180         }
7182     virtual ~TaskStaticLib()
7183         {}
7185     virtual bool execute()
7186         {
7187         //trace("###########HERE %d", fileSet.size());
7188         bool doit = false;
7189         
7190         String fullOut = parent.resolve(fileName);
7191         //trace("ar fullout: %s", fullOut.c_str());
7192         
7193         if (!listFiles(parent, fileSet))
7194             return false;
7195         String fileSetDir = fileSet.getDirectory();
7197         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7198             {
7199             String fname;
7200             if (fileSetDir.size()>0)
7201                 {
7202                 fname.append(fileSetDir);
7203                 fname.append("/");
7204                 }
7205             fname.append(fileSet[i]);
7206             String fullName = parent.resolve(fname);
7207             //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
7208             if (isNewerThan(fullName, fullOut))
7209                 doit = true;
7210             }
7211         //trace("Needs it:%d", doit);
7212         if (!doit)
7213             {
7214             return true;
7215             }
7217         String cmd = command;
7218         cmd.append(" ");
7219         cmd.append(fullOut);
7220         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7221             {
7222             String fname;
7223             if (fileSetDir.size()>0)
7224                 {
7225                 fname.append(fileSetDir);
7226                 fname.append("/");
7227                 }
7228             fname.append(fileSet[i]);
7229             String fullName = parent.resolve(fname);
7231             cmd.append(" ");
7232             cmd.append(fullName);
7233             }
7235         String outString, errString;
7236         if (!executeCommand(cmd.c_str(), "", outString, errString))
7237             {
7238             error("<staticlib> problem: %s", errString.c_str());
7239             return false;
7240             }
7242         return true;
7243         }
7246     virtual bool parse(Element *elem)
7247         {
7248         String s;
7249         if (!parent.getAttribute(elem, "command", s))
7250             return false;
7251         if (s.size()>0)
7252             command = s;
7253         if (!parent.getAttribute(elem, "file", fileName))
7254             return false;
7255             
7256         std::vector<Element *> children = elem->getChildren();
7257         for (unsigned int i=0 ; i<children.size() ; i++)
7258             {
7259             Element *child = children[i];
7260             String tagName = child->getName();
7261             if (tagName == "fileset")
7262                 {
7263                 if (!parseFileSet(child, parent, fileSet))
7264                     return false;
7265                 }
7266             }
7267         return true;
7268         }
7270 private:
7272     String command;
7273     String fileName;
7274     FileSet fileSet;
7276 };
7281 /**
7282  * Strip an executable
7283  */
7284 class TaskStrip : public Task
7286 public:
7288     TaskStrip(MakeBase &par) : Task(par)
7289         { type = TASK_STRIP; name = "strip"; }
7291     virtual ~TaskStrip()
7292         {}
7294     virtual bool execute()
7295         {
7296         String fullName = parent.resolve(fileName);
7297         //trace("fullDir:%s", fullDir.c_str());
7298         String cmd;
7299         String outbuf, errbuf;
7301         if (symFileName.size()>0)
7302             {
7303             String symFullName = parent.resolve(symFileName);
7304             cmd = "objcopy --only-keep-debug ";
7305             cmd.append(getNativePath(fullName));
7306             cmd.append(" ");
7307             cmd.append(getNativePath(symFullName));
7308             if (!executeCommand(cmd, "", outbuf, errbuf))
7309                 {
7310                 error("<strip> symbol file failed : %s", errbuf.c_str());
7311                 return false;
7312                 }
7313             }
7314             
7315         cmd = "strip ";
7316         cmd.append(getNativePath(fullName));
7317         if (!executeCommand(cmd, "", outbuf, errbuf))
7318             {
7319             error("<strip> failed : %s", errbuf.c_str());
7320             return false;
7321             }
7322         return true;
7323         }
7325     virtual bool parse(Element *elem)
7326         {
7327         if (!parent.getAttribute(elem, "file", fileName))
7328             return false;
7329         if (!parent.getAttribute(elem, "symfile", symFileName))
7330             return false;
7331         if (fileName.size() == 0)
7332             {
7333             error("<strip> requires 'file=\"fileName\"' attribute");
7334             return false;
7335             }
7336         return true;
7337         }
7339 private:
7341     String fileName;
7342     String symFileName;
7343 };
7346 /**
7347  *
7348  */
7349 class TaskTouch : public Task
7351 public:
7353     TaskTouch(MakeBase &par) : Task(par)
7354         { type = TASK_TOUCH; name = "touch"; }
7356     virtual ~TaskTouch()
7357         {}
7359     virtual bool execute()
7360         {
7361         String fullName = parent.resolve(fileName);
7362         String nativeFile = getNativePath(fullName);
7363         if (!isRegularFile(fullName) && !isDirectory(fullName))
7364             {            
7365             // S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH
7366             int ret = creat(nativeFile.c_str(), 0666);
7367             if (ret != 0) 
7368                 {
7369                 error("<touch> could not create '%s' : %s",
7370                     nativeFile.c_str(), strerror(ret));
7371                 return false;
7372                 }
7373             return true;
7374             }
7375         int ret = utime(nativeFile.c_str(), (struct utimbuf *)0);
7376         if (ret != 0)
7377             {
7378             error("<touch> could not update the modification time for '%s' : %s",
7379                 nativeFile.c_str(), strerror(ret));
7380             return false;
7381             }
7382         return true;
7383         }
7385     virtual bool parse(Element *elem)
7386         {
7387         //trace("touch parse");
7388         if (!parent.getAttribute(elem, "file", fileName))
7389             return false;
7390         if (fileName.size() == 0)
7391             {
7392             error("<touch> requires 'file=\"fileName\"' attribute");
7393             return false;
7394             }
7395         return true;
7396         }
7398     String fileName;
7399 };
7402 /**
7403  *
7404  */
7405 class TaskTstamp : public Task
7407 public:
7409     TaskTstamp(MakeBase &par) : Task(par)
7410         { type = TASK_TSTAMP; name = "tstamp"; }
7412     virtual ~TaskTstamp()
7413         {}
7415     virtual bool execute()
7416         {
7417         return true;
7418         }
7420     virtual bool parse(Element *elem)
7421         {
7422         //trace("tstamp parse");
7423         return true;
7424         }
7425 };
7429 /**
7430  *
7431  */
7432 Task *Task::createTask(Element *elem, int lineNr)
7434     String tagName = elem->getName();
7435     //trace("task:%s", tagName.c_str());
7436     Task *task = NULL;
7437     if (tagName == "cc")
7438         task = new TaskCC(parent);
7439     else if (tagName == "copy")
7440         task = new TaskCopy(parent);
7441     else if (tagName == "delete")
7442         task = new TaskDelete(parent);
7443     else if (tagName == "jar")
7444         task = new TaskJar(parent);
7445     else if (tagName == "javac")
7446         task = new TaskJavac(parent);
7447     else if (tagName == "link")
7448         task = new TaskLink(parent);
7449     else if (tagName == "makefile")
7450         task = new TaskMakeFile(parent);
7451     else if (tagName == "mkdir")
7452         task = new TaskMkDir(parent);
7453     else if (tagName == "msgfmt")
7454         task = new TaskMsgFmt(parent);
7455     else if (tagName == "ranlib")
7456         task = new TaskRanlib(parent);
7457     else if (tagName == "rc")
7458         task = new TaskRC(parent);
7459     else if (tagName == "sharedlib")
7460         task = new TaskSharedLib(parent);
7461     else if (tagName == "staticlib")
7462         task = new TaskStaticLib(parent);
7463     else if (tagName == "strip")
7464         task = new TaskStrip(parent);
7465     else if (tagName == "touch")
7466         task = new TaskTouch(parent);
7467     else if (tagName == "tstamp")
7468         task = new TaskTstamp(parent);
7469     else
7470         {
7471         error("Unknown task '%s'", tagName.c_str());
7472         return NULL;
7473         }
7475     task->setLine(lineNr);
7477     if (!task->parse(elem))
7478         {
7479         delete task;
7480         return NULL;
7481         }
7482     return task;
7487 //########################################################################
7488 //# T A R G E T
7489 //########################################################################
7491 /**
7492  *
7493  */
7494 class Target : public MakeBase
7497 public:
7499     /**
7500      *
7501      */
7502     Target(Make &par) : parent(par)
7503         { init(); }
7505     /**
7506      *
7507      */
7508     Target(const Target &other) : parent(other.parent)
7509         { init(); assign(other); }
7511     /**
7512      *
7513      */
7514     Target &operator=(const Target &other)
7515         { init(); assign(other); return *this; }
7517     /**
7518      *
7519      */
7520     virtual ~Target()
7521         { cleanup() ; }
7524     /**
7525      *
7526      */
7527     virtual Make &getParent()
7528         { return parent; }
7530     /**
7531      *
7532      */
7533     virtual String getName()
7534         { return name; }
7536     /**
7537      *
7538      */
7539     virtual void setName(const String &val)
7540         { name = val; }
7542     /**
7543      *
7544      */
7545     virtual String getDescription()
7546         { return description; }
7548     /**
7549      *
7550      */
7551     virtual void setDescription(const String &val)
7552         { description = val; }
7554     /**
7555      *
7556      */
7557     virtual void addDependency(const String &val)
7558         { deps.push_back(val); }
7560     /**
7561      *
7562      */
7563     virtual void parseDependencies(const String &val)
7564         { deps = tokenize(val, ", "); }
7566     /**
7567      *
7568      */
7569     virtual std::vector<String> &getDependencies()
7570         { return deps; }
7572     /**
7573      *
7574      */
7575     virtual String getIf()
7576         { return ifVar; }
7578     /**
7579      *
7580      */
7581     virtual void setIf(const String &val)
7582         { ifVar = val; }
7584     /**
7585      *
7586      */
7587     virtual String getUnless()
7588         { return unlessVar; }
7590     /**
7591      *
7592      */
7593     virtual void setUnless(const String &val)
7594         { unlessVar = val; }
7596     /**
7597      *
7598      */
7599     virtual void addTask(Task *val)
7600         { tasks.push_back(val); }
7602     /**
7603      *
7604      */
7605     virtual std::vector<Task *> &getTasks()
7606         { return tasks; }
7608 private:
7610     void init()
7611         {
7612         }
7614     void cleanup()
7615         {
7616         tasks.clear();
7617         }
7619     void assign(const Target &other)
7620         {
7621         //parent      = other.parent;
7622         name        = other.name;
7623         description = other.description;
7624         ifVar       = other.ifVar;
7625         unlessVar   = other.unlessVar;
7626         deps        = other.deps;
7627         tasks       = other.tasks;
7628         }
7630     Make &parent;
7632     String name;
7634     String description;
7636     String ifVar;
7638     String unlessVar;
7640     std::vector<String> deps;
7642     std::vector<Task *> tasks;
7644 };
7653 //########################################################################
7654 //# M A K E
7655 //########################################################################
7658 /**
7659  *
7660  */
7661 class Make : public MakeBase
7664 public:
7666     /**
7667      *
7668      */
7669     Make()
7670         { init(); }
7672     /**
7673      *
7674      */
7675     Make(const Make &other)
7676         { assign(other); }
7678     /**
7679      *
7680      */
7681     Make &operator=(const Make &other)
7682         { assign(other); return *this; }
7684     /**
7685      *
7686      */
7687     virtual ~Make()
7688         { cleanup(); }
7690     /**
7691      *
7692      */
7693     virtual std::map<String, Target> &getTargets()
7694         { return targets; }
7697     /**
7698      *
7699      */
7700     virtual String version()
7701         { return BUILDTOOL_VERSION; }
7703     /**
7704      * Overload a <property>
7705      */
7706     virtual bool specifyProperty(const String &name,
7707                                  const String &value);
7709     /**
7710      *
7711      */
7712     virtual bool run();
7714     /**
7715      *
7716      */
7717     virtual bool run(const String &target);
7721 private:
7723     /**
7724      *
7725      */
7726     void init();
7728     /**
7729      *
7730      */
7731     void cleanup();
7733     /**
7734      *
7735      */
7736     void assign(const Make &other);
7738     /**
7739      *
7740      */
7741     bool executeTask(Task &task);
7744     /**
7745      *
7746      */
7747     bool executeTarget(Target &target,
7748              std::set<String> &targetsCompleted);
7751     /**
7752      *
7753      */
7754     bool execute();
7756     /**
7757      *
7758      */
7759     bool checkTargetDependencies(Target &prop,
7760                     std::vector<String> &depList);
7762     /**
7763      *
7764      */
7765     bool parsePropertyFile(const String &fileName,
7766                            const String &prefix);
7768     /**
7769      *
7770      */
7771     bool parseProperty(Element *elem);
7773     /**
7774      *
7775      */
7776     bool parseFile();
7778     /**
7779      *
7780      */
7781     std::vector<String> glob(const String &pattern);
7784     //###############
7785     //# Fields
7786     //###############
7788     String projectName;
7790     String currentTarget;
7792     String defaultTarget;
7794     String specifiedTarget;
7796     String baseDir;
7798     String description;
7799     
7800     String envAlias;
7802     //std::vector<Property> properties;
7803     
7804     std::map<String, Target> targets;
7806     std::vector<Task *> allTasks;
7807     
7808     std::map<String, String> specifiedProperties;
7810 };
7813 //########################################################################
7814 //# C L A S S  M A I N T E N A N C E
7815 //########################################################################
7817 /**
7818  *
7819  */
7820 void Make::init()
7822     uri             = "build.xml";
7823     projectName     = "";
7824     currentTarget   = "";
7825     defaultTarget   = "";
7826     specifiedTarget = "";
7827     baseDir         = "";
7828     description     = "";
7829     envAlias        = "";
7830     properties.clear();
7831     for (unsigned int i = 0 ; i < allTasks.size() ; i++)
7832         delete allTasks[i];
7833     allTasks.clear();
7838 /**
7839  *
7840  */
7841 void Make::cleanup()
7843     for (unsigned int i = 0 ; i < allTasks.size() ; i++)
7844         delete allTasks[i];
7845     allTasks.clear();
7850 /**
7851  *
7852  */
7853 void Make::assign(const Make &other)
7855     uri              = other.uri;
7856     projectName      = other.projectName;
7857     currentTarget    = other.currentTarget;
7858     defaultTarget    = other.defaultTarget;
7859     specifiedTarget  = other.specifiedTarget;
7860     baseDir          = other.baseDir;
7861     description      = other.description;
7862     properties       = other.properties;
7867 //########################################################################
7868 //# U T I L I T Y    T A S K S
7869 //########################################################################
7871 /**
7872  *  Perform a file globbing
7873  */
7874 std::vector<String> Make::glob(const String &pattern)
7876     std::vector<String> res;
7877     return res;
7881 //########################################################################
7882 //# P U B L I C    A P I
7883 //########################################################################
7887 /**
7888  *
7889  */
7890 bool Make::executeTarget(Target &target,
7891              std::set<String> &targetsCompleted)
7894     String name = target.getName();
7896     //First get any dependencies for this target
7897     std::vector<String> deps = target.getDependencies();
7898     for (unsigned int i=0 ; i<deps.size() ; i++)
7899         {
7900         String dep = deps[i];
7901         //Did we do it already?  Skip
7902         if (targetsCompleted.find(dep)!=targetsCompleted.end())
7903             continue;
7904             
7905         std::map<String, Target> &tgts =
7906                target.getParent().getTargets();
7907         std::map<String, Target>::iterator iter =
7908                tgts.find(dep);
7909         if (iter == tgts.end())
7910             {
7911             error("Target '%s' dependency '%s' not found",
7912                       name.c_str(),  dep.c_str());
7913             return false;
7914             }
7915         Target depTarget = iter->second;
7916         if (!executeTarget(depTarget, targetsCompleted))
7917             {
7918             return false;
7919             }
7920         }
7922     status("## Target : %s", name.c_str());
7924     //Now let's do the tasks
7925     std::vector<Task *> &tasks = target.getTasks();
7926     for (unsigned int i=0 ; i<tasks.size() ; i++)
7927         {
7928         Task *task = tasks[i];
7929         status("---- task : %s", task->getName().c_str());
7930         if (!task->execute())
7931             {
7932             return false;
7933             }
7934         }
7935         
7936     targetsCompleted.insert(name);
7937     
7938     return true;
7943 /**
7944  *  Main execute() method.  Start here and work
7945  *  up the dependency tree 
7946  */
7947 bool Make::execute()
7949     status("######## EXECUTE");
7951     //Determine initial target
7952     if (specifiedTarget.size()>0)
7953         {
7954         currentTarget = specifiedTarget;
7955         }
7956     else if (defaultTarget.size()>0)
7957         {
7958         currentTarget = defaultTarget;
7959         }
7960     else
7961         {
7962         error("execute: no specified or default target requested");
7963         return false;
7964         }
7966     std::map<String, Target>::iterator iter =
7967                targets.find(currentTarget);
7968     if (iter == targets.end())
7969         {
7970         error("Initial target '%s' not found",
7971                  currentTarget.c_str());
7972         return false;
7973         }
7974         
7975     //Now run
7976     Target target = iter->second;
7977     std::set<String> targetsCompleted;
7978     if (!executeTarget(target, targetsCompleted))
7979         {
7980         return false;
7981         }
7983     status("######## EXECUTE COMPLETE");
7984     return true;
7990 /**
7991  *
7992  */
7993 bool Make::checkTargetDependencies(Target &target, 
7994                             std::vector<String> &depList)
7996     String tgtName = target.getName().c_str();
7997     depList.push_back(tgtName);
7999     std::vector<String> deps = target.getDependencies();
8000     for (unsigned int i=0 ; i<deps.size() ; i++)
8001         {
8002         String dep = deps[i];
8003         //First thing entered was the starting Target
8004         if (dep == depList[0])
8005             {
8006             error("Circular dependency '%s' found at '%s'",
8007                       dep.c_str(), tgtName.c_str());
8008             std::vector<String>::iterator diter;
8009             for (diter=depList.begin() ; diter!=depList.end() ; diter++)
8010                 {
8011                 error("  %s", diter->c_str());
8012                 }
8013             return false;
8014             }
8016         std::map<String, Target> &tgts =
8017                   target.getParent().getTargets();
8018         std::map<String, Target>::iterator titer = tgts.find(dep);
8019         if (titer == tgts.end())
8020             {
8021             error("Target '%s' dependency '%s' not found",
8022                       tgtName.c_str(), dep.c_str());
8023             return false;
8024             }
8025         if (!checkTargetDependencies(titer->second, depList))
8026             {
8027             return false;
8028             }
8029         }
8030     return true;
8037 static int getword(int pos, const String &inbuf, String &result)
8039     int p = pos;
8040     int len = (int)inbuf.size();
8041     String val;
8042     while (p < len)
8043         {
8044         char ch = inbuf[p];
8045         if (!isalnum(ch) && ch!='.' && ch!='_')
8046             break;
8047         val.push_back(ch);
8048         p++;
8049         }
8050     result = val;
8051     return p;
8057 /**
8058  *
8059  */
8060 bool Make::parsePropertyFile(const String &fileName,
8061                              const String &prefix)
8063     FILE *f = fopen(fileName.c_str(), "r");
8064     if (!f)
8065         {
8066         error("could not open property file %s", fileName.c_str());
8067         return false;
8068         }
8069     int linenr = 0;
8070     while (!feof(f))
8071         {
8072         char buf[256];
8073         if (!fgets(buf, 255, f))
8074             break;
8075         linenr++;
8076         String s = buf;
8077         s = trim(s);
8078         int len = s.size();
8079         if (len == 0)
8080             continue;
8081         if (s[0] == '#')
8082             continue;
8083         String key;
8084         String val;
8085         int p = 0;
8086         int p2 = getword(p, s, key);
8087         if (p2 <= p)
8088             {
8089             error("property file %s, line %d: expected keyword",
8090                     fileName.c_str(), linenr);
8091             return false;
8092             }
8093         if (prefix.size() > 0)
8094             {
8095             key.insert(0, prefix);
8096             }
8098         //skip whitespace
8099         for (p=p2 ; p<len ; p++)
8100             if (!isspace(s[p]))
8101                 break;
8103         if (p>=len || s[p]!='=')
8104             {
8105             error("property file %s, line %d: expected '='",
8106                     fileName.c_str(), linenr);
8107             return false;
8108             }
8109         p++;
8111         //skip whitespace
8112         for ( ; p<len ; p++)
8113             if (!isspace(s[p]))
8114                 break;
8116         /* This way expects a word after the =
8117         p2 = getword(p, s, val);
8118         if (p2 <= p)
8119             {
8120             error("property file %s, line %d: expected value",
8121                     fileName.c_str(), linenr);
8122             return false;
8123             }
8124         */
8125         // This way gets the rest of the line after the =
8126         if (p>=len)
8127             {
8128             error("property file %s, line %d: expected value",
8129                     fileName.c_str(), linenr);
8130             return false;
8131             }
8132         val = s.substr(p);
8133         if (key.size()==0)
8134             continue;
8135         //allow property to be set, even if val=""
8137         //trace("key:'%s' val:'%s'", key.c_str(), val.c_str());
8138         //See if we wanted to overload this property
8139         std::map<String, String>::iterator iter =
8140             specifiedProperties.find(key);
8141         if (iter!=specifiedProperties.end())
8142             {
8143             val = iter->second;
8144             status("overloading property '%s' = '%s'",
8145                    key.c_str(), val.c_str());
8146             }
8147         properties[key] = val;
8148         }
8149     fclose(f);
8150     return true;
8156 /**
8157  *
8158  */
8159 bool Make::parseProperty(Element *elem)
8161     std::vector<Attribute> &attrs = elem->getAttributes();
8162     for (unsigned int i=0 ; i<attrs.size() ; i++)
8163         {
8164         String attrName = attrs[i].getName();
8165         String attrVal  = attrs[i].getValue();
8167         if (attrName == "name")
8168             {
8169             String val;
8170             if (!getAttribute(elem, "value", val))
8171                 return false;
8172             if (val.size() > 0)
8173                 {
8174                 properties[attrVal] = val;
8175                 }
8176             else
8177                 {
8178                 if (!getAttribute(elem, "location", val))
8179                     return false;
8180                 //let the property exist, even if not defined
8181                 properties[attrVal] = val;
8182                 }
8183             //See if we wanted to overload this property
8184             std::map<String, String>::iterator iter =
8185                 specifiedProperties.find(attrVal);
8186             if (iter != specifiedProperties.end())
8187                 {
8188                 val = iter->second;
8189                 status("overloading property '%s' = '%s'",
8190                     attrVal.c_str(), val.c_str());
8191                 properties[attrVal] = val;
8192                 }
8193             }
8194         else if (attrName == "file")
8195             {
8196             String prefix;
8197             if (!getAttribute(elem, "prefix", prefix))
8198                 return false;
8199             if (prefix.size() > 0)
8200                 {
8201                 if (prefix[prefix.size()-1] != '.')
8202                     prefix.push_back('.');
8203                 }
8204             if (!parsePropertyFile(attrName, prefix))
8205                 return false;
8206             }
8207         else if (attrName == "environment")
8208             {
8209             if (envAlias.size() > 0)
8210                 {
8211                 error("environment property can only be set once");
8212                 return false;
8213                 }
8214             envAlias = attrVal;
8215             }
8216         }
8218     return true;
8224 /**
8225  *
8226  */
8227 bool Make::parseFile()
8229     status("######## PARSE : %s", uri.getPath().c_str());
8231     setLine(0);
8233     Parser parser;
8234     Element *root = parser.parseFile(uri.getNativePath());
8235     if (!root)
8236         {
8237         error("Could not open %s for reading",
8238               uri.getNativePath().c_str());
8239         return false;
8240         }
8241     
8242     setLine(root->getLine());
8244     if (root->getChildren().size()==0 ||
8245         root->getChildren()[0]->getName()!="project")
8246         {
8247         error("Main xml element should be <project>");
8248         delete root;
8249         return false;
8250         }
8252     //########## Project attributes
8253     Element *project = root->getChildren()[0];
8254     String s = project->getAttribute("name");
8255     if (s.size() > 0)
8256         projectName = s;
8257     s = project->getAttribute("default");
8258     if (s.size() > 0)
8259         defaultTarget = s;
8260     s = project->getAttribute("basedir");
8261     if (s.size() > 0)
8262         baseDir = s;
8264     //######### PARSE MEMBERS
8265     std::vector<Element *> children = project->getChildren();
8266     for (unsigned int i=0 ; i<children.size() ; i++)
8267         {
8268         Element *elem = children[i];
8269         setLine(elem->getLine());
8270         String tagName = elem->getName();
8272         //########## DESCRIPTION
8273         if (tagName == "description")
8274             {
8275             description = parser.trim(elem->getValue());
8276             }
8278         //######### PROPERTY
8279         else if (tagName == "property")
8280             {
8281             if (!parseProperty(elem))
8282                 return false;
8283             }
8285         //######### TARGET
8286         else if (tagName == "target")
8287             {
8288             String tname   = elem->getAttribute("name");
8289             String tdesc   = elem->getAttribute("description");
8290             String tdeps   = elem->getAttribute("depends");
8291             String tif     = elem->getAttribute("if");
8292             String tunless = elem->getAttribute("unless");
8293             Target target(*this);
8294             target.setName(tname);
8295             target.setDescription(tdesc);
8296             target.parseDependencies(tdeps);
8297             target.setIf(tif);
8298             target.setUnless(tunless);
8299             std::vector<Element *> telems = elem->getChildren();
8300             for (unsigned int i=0 ; i<telems.size() ; i++)
8301                 {
8302                 Element *telem = telems[i];
8303                 Task breeder(*this);
8304                 Task *task = breeder.createTask(telem, telem->getLine());
8305                 if (!task)
8306                     return false;
8307                 allTasks.push_back(task);
8308                 target.addTask(task);
8309                 }
8311             //Check name
8312             if (tname.size() == 0)
8313                 {
8314                 error("no name for target");
8315                 return false;
8316                 }
8317             //Check for duplicate name
8318             if (targets.find(tname) != targets.end())
8319                 {
8320                 error("target '%s' already defined", tname.c_str());
8321                 return false;
8322                 }
8323             //more work than targets[tname]=target, but avoids default allocator
8324             targets.insert(std::make_pair<String, Target>(tname, target));
8325             }
8326         //######### none of the above
8327         else
8328             {
8329             error("unknown toplevel tag: <%s>", tagName.c_str());
8330             return false;
8331             }
8333         }
8335     std::map<String, Target>::iterator iter;
8336     for (iter = targets.begin() ; iter!= targets.end() ; iter++)
8337         {
8338         Target tgt = iter->second;
8339         std::vector<String> depList;
8340         if (!checkTargetDependencies(tgt, depList))
8341             {
8342             return false;
8343             }
8344         }
8347     delete root;
8348     status("######## PARSE COMPLETE");
8349     return true;
8353 /**
8354  * Overload a <property>
8355  */
8356 bool Make::specifyProperty(const String &name, const String &value)
8358     if (specifiedProperties.find(name) != specifiedProperties.end())
8359         {
8360         error("Property %s already specified", name.c_str());
8361         return false;
8362         }
8363     specifiedProperties[name] = value;
8364     return true;
8369 /**
8370  *
8371  */
8372 bool Make::run()
8374     if (!parseFile())
8375         return false;
8376         
8377     if (!execute())
8378         return false;
8380     return true;
8386 /**
8387  * Get a formatted MM:SS.sss time elapsed string
8388  */ 
8389 static String
8390 timeDiffString(struct timeval &x, struct timeval &y)
8392     long microsX  = x.tv_usec;
8393     long secondsX = x.tv_sec;
8394     long microsY  = y.tv_usec;
8395     long secondsY = y.tv_sec;
8396     if (microsX < microsY)
8397         {
8398         microsX += 1000000;
8399         secondsX -= 1;
8400         }
8402     int seconds = (int)(secondsX - secondsY);
8403     int millis  = (int)((microsX - microsY)/1000);
8405     int minutes = seconds/60;
8406     seconds -= minutes*60;
8407     char buf[80];
8408     snprintf(buf, 79, "%dm %d.%03ds", minutes, seconds, millis);
8409     String ret = buf;
8410     return ret;
8411     
8414 /**
8415  *
8416  */
8417 bool Make::run(const String &target)
8419     status("####################################################");
8420     status("#   %s", version().c_str());
8421     status("####################################################");
8422     struct timeval timeStart, timeEnd;
8423     ::gettimeofday(&timeStart, NULL);
8424     specifiedTarget = target;
8425     if (!run())
8426         return false;
8427     ::gettimeofday(&timeEnd, NULL);
8428     String timeStr = timeDiffString(timeEnd, timeStart);
8429     status("####################################################");
8430     status("#   BuildTool Completed : %s", timeStr.c_str());
8431     status("####################################################");
8432     return true;
8441 }// namespace buildtool
8442 //########################################################################
8443 //# M A I N
8444 //########################################################################
8446 typedef buildtool::String String;
8448 /**
8449  *  Format an error message in printf() style
8450  */
8451 static void error(char *fmt, ...)
8453     va_list ap;
8454     va_start(ap, fmt);
8455     fprintf(stderr, "BuildTool error: ");
8456     vfprintf(stderr, fmt, ap);
8457     fprintf(stderr, "\n");
8458     va_end(ap);
8462 static bool parseProperty(const String &s, String &name, String &val)
8464     int len = s.size();
8465     int i;
8466     for (i=0 ; i<len ; i++)
8467         {
8468         char ch = s[i];
8469         if (ch == '=')
8470             break;
8471         name.push_back(ch);
8472         }
8473     if (i>=len || s[i]!='=')
8474         {
8475         error("property requires -Dname=value");
8476         return false;
8477         }
8478     i++;
8479     for ( ; i<len ; i++)
8480         {
8481         char ch = s[i];
8482         val.push_back(ch);
8483         }
8484     return true;
8488 /**
8489  * Compare a buffer with a key, for the length of the key
8490  */
8491 static bool sequ(const String &buf, char *key)
8493     int len = buf.size();
8494     for (int i=0 ; key[i] && i<len ; i++)
8495         {
8496         if (key[i] != buf[i])
8497             return false;
8498         }        
8499     return true;
8502 static void usage(int argc, char **argv)
8504     printf("usage:\n");
8505     printf("   %s [options] [target]\n", argv[0]);
8506     printf("Options:\n");
8507     printf("  -help, -h              print this message\n");
8508     printf("  -version               print the version information and exit\n");
8509     printf("  -file <file>           use given buildfile\n");
8510     printf("  -f <file>                 ''\n");
8511     printf("  -D<property>=<value>   use value for given property\n");
8517 /**
8518  * Parse the command-line args, get our options,
8519  * and run this thing
8520  */   
8521 static bool parseOptions(int argc, char **argv)
8523     if (argc < 1)
8524         {
8525         error("Cannot parse arguments");
8526         return false;
8527         }
8529     buildtool::Make make;
8531     String target;
8533     //char *progName = argv[0];
8534     for (int i=1 ; i<argc ; i++)
8535         {
8536         String arg = argv[i];
8537         if (arg.size()>1 && arg[0]=='-')
8538             {
8539             if (arg == "-h" || arg == "-help")
8540                 {
8541                 usage(argc,argv);
8542                 return true;
8543                 }
8544             else if (arg == "-version")
8545                 {
8546                 printf("%s", make.version().c_str());
8547                 return true;
8548                 }
8549             else if (arg == "-f" || arg == "-file")
8550                 {
8551                 if (i>=argc)
8552                    {
8553                    usage(argc, argv);
8554                    return false;
8555                    }
8556                 i++; //eat option
8557                 make.setURI(argv[i]);
8558                 }
8559             else if (arg.size()>2 && sequ(arg, "-D"))
8560                 {
8561                 String s = arg.substr(2, s.size());
8562                 String name, value;
8563                 if (!parseProperty(s, name, value))
8564                    {
8565                    usage(argc, argv);
8566                    return false;
8567                    }
8568                 if (!make.specifyProperty(name, value))
8569                     return false;
8570                 }
8571             else
8572                 {
8573                 error("Unknown option:%s", arg.c_str());
8574                 return false;
8575                 }
8576             }
8577         else
8578             {
8579             if (target.size()>0)
8580                 {
8581                 error("only one initial target");
8582                 usage(argc, argv);
8583                 return false;
8584                 }
8585             target = arg;
8586             }
8587         }
8589     //We have the options.  Now execute them
8590     if (!make.run(target))
8591         return false;
8593     return true;
8599 /*
8600 static bool runMake()
8602     buildtool::Make make;
8603     if (!make.run())
8604         return false;
8605     return true;
8609 static bool pkgConfigTest()
8611     buildtool::PkgConfig pkgConfig;
8612     if (!pkgConfig.readFile("gtk+-2.0.pc"))
8613         return false;
8614     return true;
8619 static bool depTest()
8621     buildtool::DepTool deptool;
8622     deptool.setSourceDirectory("/dev/ink/inkscape/src");
8623     if (!deptool.generateDependencies("build.dep"))
8624         return false;
8625     std::vector<buildtool::DepRec> res =
8626            deptool.loadDepFile("build.dep");
8627     if (res.size() == 0)
8628         return false;
8629     return true;
8632 static bool popenTest()
8634     buildtool::Make make;
8635     buildtool::String out, err;
8636     bool ret = make.executeCommand("gcc xx.cpp", "", out, err);
8637     printf("Popen test:%d '%s' '%s'\n", ret, out.c_str(), err.c_str());
8638     return true;
8642 static bool propFileTest()
8644     buildtool::Make make;
8645     make.parsePropertyFile("test.prop", "test.");
8646     return true;
8648 */
8650 int main(int argc, char **argv)
8653     if (!parseOptions(argc, argv))
8654         return 1;
8655     /*
8656     if (!popenTest())
8657         return 1;
8659     if (!depTest())
8660         return 1;
8661     if (!propFileTest())
8662         return 1;
8663     if (runMake())
8664         return 1;
8665     */
8666     return 0;
8670 //########################################################################
8671 //# E N D 
8672 //########################################################################