Code

improve error messages
[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             trace("BEFORE");
5993             bool ret = executeCommand(cmd.c_str(), "", outString, errString);
5994             trace("AFTER");
5996             if (f)
5997                 {
5998                 fprintf(f, "########################### File : %s\n",
5999                              srcFullName.c_str());
6000                 fprintf(f, "#### COMMAND ###\n");
6001                 int col = 0;
6002                 for (int i = 0 ; i < cmd.size() ; i++)
6003                     {
6004                     char ch = cmd[i];
6005                     if (isspace(ch)  && col > 63)
6006                         {
6007                         fputc('\n', f);
6008                         col = 0;
6009                         }
6010                     else
6011                         {
6012                         fputc(ch, f);
6013                         col++;
6014                         }
6015                     if (col > 76)
6016                         {
6017                         fputc('\n', f);
6018                         col = 0;
6019                         }
6020                     }
6021                 fprintf(f, "\n");
6022                 fprintf(f, "#### STDOUT ###\n%s\n", outString.c_str());
6023                 fprintf(f, "#### STDERR ###\n%s\n\n", errString.c_str());
6024                 }
6025             if (!ret)
6026                 {
6027                 error("problem compiling: %s", errString.c_str());
6028                 return false;
6029                 }
6030                 
6031             }
6033         if (f)
6034             {
6035             fclose(f);
6036             }
6037         
6038         return true;
6039         }
6041     virtual bool parse(Element *elem)
6042         {
6043         String s;
6044         if (!parent.getAttribute(elem, "command", s))
6045             return false;
6046         if (s.size()>0) { ccCommand = s; cxxCommand = s; }
6047         if (!parent.getAttribute(elem, "cc", s))
6048             return false;
6049         if (s.size()>0) ccCommand = s;
6050         if (!parent.getAttribute(elem, "cxx", s))
6051             return false;
6052         if (s.size()>0) cxxCommand = s;
6053         if (!parent.getAttribute(elem, "destdir", s))
6054             return false;
6055         if (s.size()>0) dest = s;
6057         std::vector<Element *> children = elem->getChildren();
6058         for (unsigned int i=0 ; i<children.size() ; i++)
6059             {
6060             Element *child = children[i];
6061             String tagName = child->getName();
6062             if (tagName == "flags")
6063                 {
6064                 if (!parent.getValue(child, flags))
6065                     return false;
6066                 flags = strip(flags);
6067                 }
6068             else if (tagName == "includes")
6069                 {
6070                 if (!parent.getValue(child, includes))
6071                     return false;
6072                 includes = strip(includes);
6073                 }
6074             else if (tagName == "defines")
6075                 {
6076                 if (!parent.getValue(child, defines))
6077                     return false;
6078                 defines = strip(defines);
6079                 }
6080             else if (tagName == "fileset")
6081                 {
6082                 if (!parseFileSet(child, parent, fileSet))
6083                     return false;
6084                 source = fileSet.getDirectory();
6085                 }
6086             }
6088         return true;
6089         }
6090         
6091 protected:
6093     String ccCommand;
6094     String cxxCommand;
6095     String source;
6096     String dest;
6097     String flags;
6098     String defines;
6099     String includes;
6100     FileSet fileSet;
6101     
6102 };
6106 /**
6107  *
6108  */
6109 class TaskCopy : public Task
6111 public:
6113     typedef enum
6114         {
6115         CP_NONE,
6116         CP_TOFILE,
6117         CP_TODIR
6118         } CopyType;
6120     TaskCopy(MakeBase &par) : Task(par)
6121         {
6122         type = TASK_COPY; name = "copy";
6123         cptype = CP_NONE;
6124         verbose = false;
6125         haveFileSet = false;
6126         }
6128     virtual ~TaskCopy()
6129         {}
6131     virtual bool execute()
6132         {
6133         switch (cptype)
6134            {
6135            case CP_TOFILE:
6136                {
6137                if (fileName.size()>0)
6138                    {
6139                    status("          : %s to %s",
6140                         fileName.c_str(), toFileName.c_str());
6141                    String fullSource = parent.resolve(fileName);
6142                    String fullDest = parent.resolve(toFileName);
6143                    //trace("copy %s to file %s", fullSource.c_str(),
6144                    //                       fullDest.c_str());
6145                    if (!isRegularFile(fullSource))
6146                        {
6147                        error("copy : file %s does not exist", fullSource.c_str());
6148                        return false;
6149                        }
6150                    if (!isNewerThan(fullSource, fullDest))
6151                        {
6152                        return true;
6153                        }
6154                    if (!copyFile(fullSource, fullDest))
6155                        return false;
6156                    status("          : 1 file copied");
6157                    }
6158                return true;
6159                }
6160            case CP_TODIR:
6161                {
6162                if (haveFileSet)
6163                    {
6164                    if (!listFiles(parent, fileSet))
6165                        return false;
6166                    String fileSetDir = fileSet.getDirectory();
6168                    status("          : %s to %s",
6169                        fileSetDir.c_str(), toDirName.c_str());
6171                    int nrFiles = 0;
6172                    for (unsigned int i=0 ; i<fileSet.size() ; i++)
6173                        {
6174                        String fileName = fileSet[i];
6176                        String sourcePath;
6177                        if (fileSetDir.size()>0)
6178                            {
6179                            sourcePath.append(fileSetDir);
6180                            sourcePath.append("/");
6181                            }
6182                        sourcePath.append(fileName);
6183                        String fullSource = parent.resolve(sourcePath);
6184                        
6185                        //Get the immediate parent directory's base name
6186                        String baseFileSetDir = fileSetDir;
6187                        unsigned int pos = baseFileSetDir.find_last_of('/');
6188                        if (pos!=baseFileSetDir.npos &&
6189                                   pos < baseFileSetDir.size()-1)
6190                            baseFileSetDir =
6191                               baseFileSetDir.substr(pos+1,
6192                                    baseFileSetDir.size());
6193                        //Now make the new path
6194                        String destPath;
6195                        if (toDirName.size()>0)
6196                            {
6197                            destPath.append(toDirName);
6198                            destPath.append("/");
6199                            }
6200                        if (baseFileSetDir.size()>0)
6201                            {
6202                            destPath.append(baseFileSetDir);
6203                            destPath.append("/");
6204                            }
6205                        destPath.append(fileName);
6206                        String fullDest = parent.resolve(destPath);
6207                        //trace("fileName:%s", fileName.c_str());
6208                        //trace("copy %s to new dir : %s", fullSource.c_str(),
6209                        //                   fullDest.c_str());
6210                        if (!isNewerThan(fullSource, fullDest))
6211                            {
6212                            //trace("copy skipping %s", fullSource.c_str());
6213                            continue;
6214                            }
6215                        if (!copyFile(fullSource, fullDest))
6216                            return false;
6217                        nrFiles++;
6218                        }
6219                    status("          : %d file(s) copied", nrFiles);
6220                    }
6221                else //file source
6222                    {
6223                    //For file->dir we want only the basename of
6224                    //the source appended to the dest dir
6225                    status("          : %s to %s", 
6226                        fileName.c_str(), toDirName.c_str());
6227                    String baseName = fileName;
6228                    unsigned int pos = baseName.find_last_of('/');
6229                    if (pos!=baseName.npos && pos<baseName.size()-1)
6230                        baseName = baseName.substr(pos+1, baseName.size());
6231                    String fullSource = parent.resolve(fileName);
6232                    String destPath;
6233                    if (toDirName.size()>0)
6234                        {
6235                        destPath.append(toDirName);
6236                        destPath.append("/");
6237                        }
6238                    destPath.append(baseName);
6239                    String fullDest = parent.resolve(destPath);
6240                    //trace("copy %s to new dir : %s", fullSource.c_str(),
6241                    //                       fullDest.c_str());
6242                    if (!isRegularFile(fullSource))
6243                        {
6244                        error("copy : file %s does not exist", fullSource.c_str());
6245                        return false;
6246                        }
6247                    if (!isNewerThan(fullSource, fullDest))
6248                        {
6249                        return true;
6250                        }
6251                    if (!copyFile(fullSource, fullDest))
6252                        return false;
6253                    status("          : 1 file copied");
6254                    }
6255                return true;
6256                }
6257            }
6258         return true;
6259         }
6262     virtual bool parse(Element *elem)
6263         {
6264         if (!parent.getAttribute(elem, "file", fileName))
6265             return false;
6266         if (!parent.getAttribute(elem, "tofile", toFileName))
6267             return false;
6268         if (toFileName.size() > 0)
6269             cptype = CP_TOFILE;
6270         if (!parent.getAttribute(elem, "todir", toDirName))
6271             return false;
6272         if (toDirName.size() > 0)
6273             cptype = CP_TODIR;
6274         String ret;
6275         if (!parent.getAttribute(elem, "verbose", ret))
6276             return false;
6277         if (ret.size()>0 && !getBool(ret, verbose))
6278             return false;
6279             
6280         haveFileSet = false;
6281         
6282         std::vector<Element *> children = elem->getChildren();
6283         for (unsigned int i=0 ; i<children.size() ; i++)
6284             {
6285             Element *child = children[i];
6286             String tagName = child->getName();
6287             if (tagName == "fileset")
6288                 {
6289                 if (!parseFileSet(child, parent, fileSet))
6290                     {
6291                     error("problem getting fileset");
6292                     return false;
6293                     }
6294                 haveFileSet = true;
6295                 }
6296             }
6298         //Perform validity checks
6299         if (fileName.size()>0 && fileSet.size()>0)
6300             {
6301             error("<copy> can only have one of : file= and <fileset>");
6302             return false;
6303             }
6304         if (toFileName.size()>0 && toDirName.size()>0)
6305             {
6306             error("<copy> can only have one of : tofile= or todir=");
6307             return false;
6308             }
6309         if (haveFileSet && toDirName.size()==0)
6310             {
6311             error("a <copy> task with a <fileset> must have : todir=");
6312             return false;
6313             }
6314         if (cptype == CP_TOFILE && fileName.size()==0)
6315             {
6316             error("<copy> tofile= must be associated with : file=");
6317             return false;
6318             }
6319         if (cptype == CP_TODIR && fileName.size()==0 && !haveFileSet)
6320             {
6321             error("<copy> todir= must be associated with : file= or <fileset>");
6322             return false;
6323             }
6325         return true;
6326         }
6327         
6328 private:
6330     int cptype;
6331     String fileName;
6332     FileSet fileSet;
6333     String toFileName;
6334     String toDirName;
6335     bool verbose;
6336     bool haveFileSet;
6337 };
6340 /**
6341  *
6342  */
6343 class TaskDelete : public Task
6345 public:
6347     typedef enum
6348         {
6349         DEL_FILE,
6350         DEL_DIR,
6351         DEL_FILESET
6352         } DeleteType;
6354     TaskDelete(MakeBase &par) : Task(par)
6355         { 
6356           type        = TASK_DELETE;
6357           name        = "delete";
6358           delType     = DEL_FILE;
6359           verbose     = false;
6360           quiet       = false;
6361           failOnError = true;
6362         }
6364     virtual ~TaskDelete()
6365         {}
6367     virtual bool execute()
6368         {
6369         struct stat finfo;
6370         switch (delType)
6371             {
6372             case DEL_FILE:
6373                 {
6374                 status("          : %s", fileName.c_str());
6375                 String fullName = parent.resolve(fileName);
6376                 char *fname = (char *)fullName.c_str();
6377                 //does not exist
6378                 if (stat(fname, &finfo)<0)
6379                     return true;
6380                 //exists but is not a regular file
6381                 if (!S_ISREG(finfo.st_mode))
6382                     {
6383                     error("<delete> failed. '%s' exists and is not a regular file",
6384                           fname);
6385                     return false;
6386                     }
6387                 if (remove(fname)<0)
6388                     {
6389                     error("<delete> failed: %s", strerror(errno));
6390                     return false;
6391                     }
6392                 return true;
6393                 }
6394             case DEL_DIR:
6395                 {
6396                 status("          : %s", dirName.c_str());
6397                 String fullDir = parent.resolve(dirName);
6398                 if (!removeDirectory(fullDir))
6399                     return false;
6400                 return true;
6401                 }
6402             }
6403         return true;
6404         }
6406     virtual bool parse(Element *elem)
6407         {
6408         if (!parent.getAttribute(elem, "file", fileName))
6409             return false;
6410         if (fileName.size() > 0)
6411             delType = DEL_FILE;
6412         if (!parent.getAttribute(elem, "dir", dirName))
6413             return false;
6414         if (dirName.size() > 0)
6415             delType = DEL_DIR;
6416         if (fileName.size()>0 && dirName.size()>0)
6417             {
6418             error("<delete> can have one attribute of file= or dir=");
6419             return false;
6420             }
6421         if (fileName.size()==0 && dirName.size()==0)
6422             {
6423             error("<delete> must have one attribute of file= or dir=");
6424             return false;
6425             }
6426         String ret;
6427         if (!parent.getAttribute(elem, "verbose", ret))
6428             return false;
6429         if (ret.size()>0 && !getBool(ret, verbose))
6430             return false;
6431         if (!parent.getAttribute(elem, "quiet", ret))
6432             return false;
6433         if (ret.size()>0 && !getBool(ret, quiet))
6434             return false;
6435         if (!parent.getAttribute(elem, "failonerror", ret))
6436             return false;
6437         if (ret.size()>0 && !getBool(ret, failOnError))
6438             return false;
6439         return true;
6440         }
6442 private:
6444     int delType;
6445     String dirName;
6446     String fileName;
6447     bool verbose;
6448     bool quiet;
6449     bool failOnError;
6450 };
6453 /**
6454  *
6455  */
6456 class TaskJar : public Task
6458 public:
6460     TaskJar(MakeBase &par) : Task(par)
6461         { type = TASK_JAR; name = "jar"; }
6463     virtual ~TaskJar()
6464         {}
6466     virtual bool execute()
6467         {
6468         return true;
6469         }
6471     virtual bool parse(Element *elem)
6472         {
6473         return true;
6474         }
6475 };
6478 /**
6479  *
6480  */
6481 class TaskJavac : public Task
6483 public:
6485     TaskJavac(MakeBase &par) : Task(par)
6486         { type = TASK_JAVAC; name = "javac"; }
6488     virtual ~TaskJavac()
6489         {}
6491     virtual bool execute()
6492         {
6493         return true;
6494         }
6496     virtual bool parse(Element *elem)
6497         {
6498         return true;
6499         }
6500 };
6503 /**
6504  *
6505  */
6506 class TaskLink : public Task
6508 public:
6510     TaskLink(MakeBase &par) : Task(par)
6511         {
6512         type = TASK_LINK; name = "link";
6513         command = "g++";
6514         doStrip = false;
6515                 stripCommand = "strip";
6516                 objcopyCommand = "objcopy";
6517         }
6519     virtual ~TaskLink()
6520         {}
6522     virtual bool execute()
6523         {
6524         if (!listFiles(parent, fileSet))
6525             return false;
6526         String fileSetDir = fileSet.getDirectory();
6527         //trace("%d files in %s", fileSet.size(), fileSetDir.c_str());
6528         bool doit = false;
6529         String fullTarget = parent.resolve(fileName);
6530         String cmd = command;
6531         cmd.append(" -o ");
6532         cmd.append(fullTarget);
6533         cmd.append(" ");
6534         cmd.append(flags);
6535         for (unsigned int i=0 ; i<fileSet.size() ; i++)
6536             {
6537             cmd.append(" ");
6538             String obj;
6539             if (fileSetDir.size()>0)
6540                 {
6541                 obj.append(fileSetDir);
6542                 obj.append("/");
6543                 }
6544             obj.append(fileSet[i]);
6545             String fullObj = parent.resolve(obj);
6546             cmd.append(fullObj);
6547             //trace("link: tgt:%s obj:%s", fullTarget.c_str(),
6548             //          fullObj.c_str());
6549             if (isNewerThan(fullObj, fullTarget))
6550                 doit = true;
6551             }
6552         cmd.append(" ");
6553         cmd.append(libs);
6554         if (!doit)
6555             {
6556             //trace("link not needed");
6557             return true;
6558             }
6559         //trace("LINK cmd:%s", cmd.c_str());
6562         String outbuf, errbuf;
6563         if (!executeCommand(cmd.c_str(), "", outbuf, errbuf))
6564             {
6565             error("LINK problem: %s", errbuf.c_str());
6566             return false;
6567             }
6569         if (symFileName.size()>0)
6570             {
6571             String symFullName = parent.resolve(symFileName);
6572             cmd = objcopyCommand;
6573             cmd.append(" --only-keep-debug ");
6574             cmd.append(getNativePath(fullTarget));
6575             cmd.append(" ");
6576             cmd.append(getNativePath(symFullName));
6577             if (!executeCommand(cmd, "", outbuf, errbuf))
6578                 {
6579                 error("<strip> symbol file failed : %s", errbuf.c_str());
6580                 return false;
6581                 }
6582             }
6583             
6584         if (doStrip)
6585             {
6586             cmd = stripCommand;
6587             cmd.append(" ");
6588             cmd.append(getNativePath(fullTarget));
6589             if (!executeCommand(cmd, "", outbuf, errbuf))
6590                {
6591                error("<strip> failed : %s", errbuf.c_str());
6592                return false;
6593                }
6594             }
6596         return true;
6597         }
6599     virtual bool parse(Element *elem)
6600         {
6601         String s;
6602         if (!parent.getAttribute(elem, "command", s))
6603             return false;
6604         if (s.size()>0)
6605             command = s;
6606         if (!parent.getAttribute(elem, "objcopycommand", s))
6607             return false;
6608         if (s.size()>0)
6609             objcopyCommand = s;
6610         if (!parent.getAttribute(elem, "stripcommand", s))
6611             return false;
6612         if (s.size()>0)
6613             stripCommand = s;
6614         if (!parent.getAttribute(elem, "out", fileName))
6615             return false;
6616         if (!parent.getAttribute(elem, "strip", s))
6617             return false;
6618         if (s.size()>0 && !getBool(s, doStrip))
6619             return false;
6620         if (!parent.getAttribute(elem, "symfile", symFileName))
6621             return false;
6622             
6623         std::vector<Element *> children = elem->getChildren();
6624         for (unsigned int i=0 ; i<children.size() ; i++)
6625             {
6626             Element *child = children[i];
6627             String tagName = child->getName();
6628             if (tagName == "fileset")
6629                 {
6630                 if (!parseFileSet(child, parent, fileSet))
6631                     return false;
6632                 }
6633             else if (tagName == "flags")
6634                 {
6635                 if (!parent.getValue(child, flags))
6636                     return false;
6637                 flags = strip(flags);
6638                 }
6639             else if (tagName == "libs")
6640                 {
6641                 if (!parent.getValue(child, libs))
6642                     return false;
6643                 libs = strip(libs);
6644                 }
6645             }
6646         return true;
6647         }
6649 private:
6651     String  command;
6652     String  fileName;
6653     String  flags;
6654     String  libs;
6655     FileSet fileSet;
6656     bool    doStrip;
6657     String  symFileName;
6658     String  stripCommand;
6659     String  objcopyCommand;
6661 };
6665 /**
6666  * Create a named directory
6667  */
6668 class TaskMakeFile : public Task
6670 public:
6672     TaskMakeFile(MakeBase &par) : Task(par)
6673         { type = TASK_MAKEFILE; name = "makefile"; }
6675     virtual ~TaskMakeFile()
6676         {}
6678     virtual bool execute()
6679         {
6680         status("          : %s", fileName.c_str());
6681         String fullName = parent.resolve(fileName);
6682         if (!isNewerThan(parent.getURI().getPath(), fullName))
6683             {
6684             //trace("skipped <makefile>");
6685             return true;
6686             }
6687         //trace("fullName:%s", fullName.c_str());
6688         FILE *f = fopen(fullName.c_str(), "w");
6689         if (!f)
6690             {
6691             error("<makefile> could not open %s for writing : %s",
6692                 fullName.c_str(), strerror(errno));
6693             return false;
6694             }
6695         for (unsigned int i=0 ; i<text.size() ; i++)
6696             fputc(text[i], f);
6697         fputc('\n', f);
6698         fclose(f);
6699         return true;
6700         }
6702     virtual bool parse(Element *elem)
6703         {
6704         if (!parent.getAttribute(elem, "file", fileName))
6705             return false;
6706         if (fileName.size() == 0)
6707             {
6708             error("<makefile> requires 'file=\"filename\"' attribute");
6709             return false;
6710             }
6711         if (!parent.getValue(elem, text))
6712             return false;
6713         text = leftJustify(text);
6714         //trace("dirname:%s", dirName.c_str());
6715         return true;
6716         }
6718 private:
6720     String fileName;
6721     String text;
6722 };
6726 /**
6727  * Create a named directory
6728  */
6729 class TaskMkDir : public Task
6731 public:
6733     TaskMkDir(MakeBase &par) : Task(par)
6734         { type = TASK_MKDIR; name = "mkdir"; }
6736     virtual ~TaskMkDir()
6737         {}
6739     virtual bool execute()
6740         {
6741         status("          : %s", dirName.c_str());
6742         String fullDir = parent.resolve(dirName);
6743         //trace("fullDir:%s", fullDir.c_str());
6744         if (!createDirectory(fullDir))
6745             return false;
6746         return true;
6747         }
6749     virtual bool parse(Element *elem)
6750         {
6751         if (!parent.getAttribute(elem, "dir", dirName))
6752             return false;
6753         if (dirName.size() == 0)
6754             {
6755             error("<mkdir> requires 'dir=\"dirname\"' attribute");
6756             return false;
6757             }
6758         return true;
6759         }
6761 private:
6763     String dirName;
6764 };
6768 /**
6769  * Create a named directory
6770  */
6771 class TaskMsgFmt: public Task
6773 public:
6775     TaskMsgFmt(MakeBase &par) : Task(par)
6776          {
6777          type    = TASK_MSGFMT;
6778          name    = "msgfmt";
6779          command = "msgfmt";
6780          owndir  = false;
6781          outName = "";
6782          }
6784     virtual ~TaskMsgFmt()
6785         {}
6787     virtual bool execute()
6788         {
6789         if (!listFiles(parent, fileSet))
6790             return false;
6791         String fileSetDir = fileSet.getDirectory();
6793         //trace("msgfmt: %d", fileSet.size());
6794         for (unsigned int i=0 ; i<fileSet.size() ; i++)
6795             {
6796             String fileName = fileSet[i];
6797             if (getSuffix(fileName) != "po")
6798                 continue;
6799             String sourcePath;
6800             if (fileSetDir.size()>0)
6801                 {
6802                 sourcePath.append(fileSetDir);
6803                 sourcePath.append("/");
6804                 }
6805             sourcePath.append(fileName);
6806             String fullSource = parent.resolve(sourcePath);
6808             String destPath;
6809             if (toDirName.size()>0)
6810                 {
6811                 destPath.append(toDirName);
6812                 destPath.append("/");
6813                 }
6814             if (owndir)
6815                 {
6816                 String subdir = fileName;
6817                 unsigned int pos = subdir.find_last_of('.');
6818                 if (pos != subdir.npos)
6819                     subdir = subdir.substr(0, pos);
6820                 destPath.append(subdir);
6821                 destPath.append("/");
6822                 }
6823             //Pick the output file name
6824             if (outName.size() > 0)
6825                 {
6826                 destPath.append(outName);
6827                 }
6828             else
6829                 {
6830                 destPath.append(fileName);
6831                 destPath[destPath.size()-2] = 'm';
6832                 }
6834             String fullDest = parent.resolve(destPath);
6836             if (!isNewerThan(fullSource, fullDest))
6837                 {
6838                 //trace("skip %s", fullSource.c_str());
6839                 continue;
6840                 }
6841                 
6842             String cmd = command;
6843             cmd.append(" ");
6844             cmd.append(fullSource);
6845             cmd.append(" -o ");
6846             cmd.append(fullDest);
6847             
6848             int pos = fullDest.find_last_of('/');
6849             if (pos>0)
6850                 {
6851                 String fullDestPath = fullDest.substr(0, pos);
6852                 if (!createDirectory(fullDestPath))
6853                     return false;
6854                 }
6858             String outString, errString;
6859             if (!executeCommand(cmd.c_str(), "", outString, errString))
6860                 {
6861                 error("<msgfmt> problem: %s", errString.c_str());
6862                 return false;
6863                 }
6864             }
6866         return true;
6867         }
6869     virtual bool parse(Element *elem)
6870         {
6871         String s;
6872         if (!parent.getAttribute(elem, "command", s))
6873             return false;
6874         if (s.size()>0)
6875             command = s;
6876         if (!parent.getAttribute(elem, "todir", toDirName))
6877             return false;
6878         if (!parent.getAttribute(elem, "out", outName))
6879             return false;
6880         if (!parent.getAttribute(elem, "owndir", s))
6881             return false;
6882         if (s.size()>0 && !getBool(s, owndir))
6883             return false;
6884             
6885         std::vector<Element *> children = elem->getChildren();
6886         for (unsigned int i=0 ; i<children.size() ; i++)
6887             {
6888             Element *child = children[i];
6889             String tagName = child->getName();
6890             if (tagName == "fileset")
6891                 {
6892                 if (!parseFileSet(child, parent, fileSet))
6893                     return false;
6894                 }
6895             }
6896         return true;
6897         }
6899 private:
6901     String  command;
6902     String  toDirName;
6903     String  outName;
6904     FileSet fileSet;
6905     bool    owndir;
6907 };
6913 /**
6914  *  Process an archive to allow random access
6915  */
6916 class TaskRanlib : public Task
6918 public:
6920     TaskRanlib(MakeBase &par) : Task(par)
6921         {
6922         type = TASK_RANLIB; name = "ranlib";
6923         command = "ranlib";
6924         }
6926     virtual ~TaskRanlib()
6927         {}
6929     virtual bool execute()
6930         {
6931         String fullName = parent.resolve(fileName);
6932         //trace("fullDir:%s", fullDir.c_str());
6933         String cmd = command;
6934         cmd.append(" ");
6935         cmd.append(fullName);
6936         String outbuf, errbuf;
6937         if (!executeCommand(cmd, "", outbuf, errbuf))
6938             return false;
6939         return true;
6940         }
6942     virtual bool parse(Element *elem)
6943         {
6944         String s;
6945         if (!parent.getAttribute(elem, "command", s))
6946             return false;
6947         if (s.size()>0)
6948            command = s;
6949         if (!parent.getAttribute(elem, "file", fileName))
6950             return false;
6951         if (fileName.size() == 0)
6952             {
6953             error("<ranlib> requires 'file=\"fileNname\"' attribute");
6954             return false;
6955             }
6956         return true;
6957         }
6959 private:
6961     String fileName;
6962     String command;
6963 };
6967 /**
6968  * Run the "ar" command to archive .o's into a .a
6969  */
6970 class TaskRC : public Task
6972 public:
6974     TaskRC(MakeBase &par) : Task(par)
6975         {
6976         type = TASK_RC; name = "rc";
6977         command = "windres";
6978         }
6980     virtual ~TaskRC()
6981         {}
6983     virtual bool execute()
6984         {
6985         String fullFile = parent.resolve(fileName);
6986         String fullOut  = parent.resolve(outName);
6987         if (!isNewerThan(fullFile, fullOut))
6988             return true;
6989         String cmd = command;
6990         cmd.append(" -o ");
6991         cmd.append(fullOut);
6992         cmd.append(" ");
6993         cmd.append(flags);
6994         cmd.append(" ");
6995         cmd.append(fullFile);
6997         String outString, errString;
6998         if (!executeCommand(cmd.c_str(), "", outString, errString))
6999             {
7000             error("RC problem: %s", errString.c_str());
7001             return false;
7002             }
7003         return true;
7004         }
7006     virtual bool parse(Element *elem)
7007         {
7008         if (!parent.getAttribute(elem, "command", command))
7009             return false;
7010         if (!parent.getAttribute(elem, "file", fileName))
7011             return false;
7012         if (!parent.getAttribute(elem, "out", outName))
7013             return false;
7014         std::vector<Element *> children = elem->getChildren();
7015         for (unsigned int i=0 ; i<children.size() ; i++)
7016             {
7017             Element *child = children[i];
7018             String tagName = child->getName();
7019             if (tagName == "flags")
7020                 {
7021                 if (!parent.getValue(child, flags))
7022                     return false;
7023                 }
7024             }
7025         return true;
7026         }
7028 private:
7030     String command;
7031     String flags;
7032     String fileName;
7033     String outName;
7035 };
7039 /**
7040  *  Collect .o's into a .so or DLL
7041  */
7042 class TaskSharedLib : public Task
7044 public:
7046     TaskSharedLib(MakeBase &par) : Task(par)
7047         {
7048         type = TASK_SHAREDLIB; name = "dll";
7049         command = "ar crv";
7050         }
7052     virtual ~TaskSharedLib()
7053         {}
7055     virtual bool execute()
7056         {
7057         //trace("###########HERE %d", fileSet.size());
7058         bool doit = false;
7059         
7060         String fullOut = parent.resolve(fileName);
7061         //trace("ar fullout: %s", fullOut.c_str());
7062         
7063         if (!listFiles(parent, fileSet))
7064             return false;
7065         String fileSetDir = fileSet.getDirectory();
7067         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7068             {
7069             String fname;
7070             if (fileSetDir.size()>0)
7071                 {
7072                 fname.append(fileSetDir);
7073                 fname.append("/");
7074                 }
7075             fname.append(fileSet[i]);
7076             String fullName = parent.resolve(fname);
7077             //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
7078             if (isNewerThan(fullName, fullOut))
7079                 doit = true;
7080             }
7081         //trace("Needs it:%d", doit);
7082         if (!doit)
7083             {
7084             return true;
7085             }
7087         String cmd = "dllwrap";
7088         cmd.append(" -o ");
7089         cmd.append(fullOut);
7090         if (defFileName.size()>0)
7091             {
7092             cmd.append(" --def ");
7093             cmd.append(defFileName);
7094             cmd.append(" ");
7095             }
7096         if (impFileName.size()>0)
7097             {
7098             cmd.append(" --implib ");
7099             cmd.append(impFileName);
7100             cmd.append(" ");
7101             }
7102         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7103             {
7104             String fname;
7105             if (fileSetDir.size()>0)
7106                 {
7107                 fname.append(fileSetDir);
7108                 fname.append("/");
7109                 }
7110             fname.append(fileSet[i]);
7111             String fullName = parent.resolve(fname);
7113             cmd.append(" ");
7114             cmd.append(fullName);
7115             }
7116         cmd.append(" ");
7117         cmd.append(libs);
7119         String outString, errString;
7120         if (!executeCommand(cmd.c_str(), "", outString, errString))
7121             {
7122             error("<sharedlib> problem: %s", errString.c_str());
7123             return false;
7124             }
7126         return true;
7127         }
7129     virtual bool parse(Element *elem)
7130         {
7131         if (!parent.getAttribute(elem, "file", fileName))
7132             return false;
7133         if (!parent.getAttribute(elem, "import", impFileName))
7134             return false;
7135         if (!parent.getAttribute(elem, "def", defFileName))
7136             return false;
7137             
7138         std::vector<Element *> children = elem->getChildren();
7139         for (unsigned int i=0 ; i<children.size() ; i++)
7140             {
7141             Element *child = children[i];
7142             String tagName = child->getName();
7143             if (tagName == "fileset")
7144                 {
7145                 if (!parseFileSet(child, parent, fileSet))
7146                     return false;
7147                 }
7148             else if (tagName == "libs")
7149                 {
7150                 if (!parent.getValue(child, libs))
7151                     return false;
7152                 libs = strip(libs);
7153                 }
7154             }
7155         return true;
7156         }
7158 private:
7160     String command;
7161     String fileName;
7162     String defFileName;
7163     String impFileName;
7164     FileSet fileSet;
7165     String libs;
7167 };
7171 /**
7172  * Run the "ar" command to archive .o's into a .a
7173  */
7174 class TaskStaticLib : public Task
7176 public:
7178     TaskStaticLib(MakeBase &par) : Task(par)
7179         {
7180         type = TASK_STATICLIB; name = "staticlib";
7181         command = "ar crv";
7182         }
7184     virtual ~TaskStaticLib()
7185         {}
7187     virtual bool execute()
7188         {
7189         //trace("###########HERE %d", fileSet.size());
7190         bool doit = false;
7191         
7192         String fullOut = parent.resolve(fileName);
7193         //trace("ar fullout: %s", fullOut.c_str());
7194         
7195         if (!listFiles(parent, fileSet))
7196             return false;
7197         String fileSetDir = fileSet.getDirectory();
7199         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7200             {
7201             String fname;
7202             if (fileSetDir.size()>0)
7203                 {
7204                 fname.append(fileSetDir);
7205                 fname.append("/");
7206                 }
7207             fname.append(fileSet[i]);
7208             String fullName = parent.resolve(fname);
7209             //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
7210             if (isNewerThan(fullName, fullOut))
7211                 doit = true;
7212             }
7213         //trace("Needs it:%d", doit);
7214         if (!doit)
7215             {
7216             return true;
7217             }
7219         String cmd = command;
7220         cmd.append(" ");
7221         cmd.append(fullOut);
7222         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7223             {
7224             String fname;
7225             if (fileSetDir.size()>0)
7226                 {
7227                 fname.append(fileSetDir);
7228                 fname.append("/");
7229                 }
7230             fname.append(fileSet[i]);
7231             String fullName = parent.resolve(fname);
7233             cmd.append(" ");
7234             cmd.append(fullName);
7235             }
7237         String outString, errString;
7238         if (!executeCommand(cmd.c_str(), "", outString, errString))
7239             {
7240             error("<staticlib> problem: %s", errString.c_str());
7241             return false;
7242             }
7244         return true;
7245         }
7248     virtual bool parse(Element *elem)
7249         {
7250         String s;
7251         if (!parent.getAttribute(elem, "command", s))
7252             return false;
7253         if (s.size()>0)
7254             command = s;
7255         if (!parent.getAttribute(elem, "file", fileName))
7256             return false;
7257             
7258         std::vector<Element *> children = elem->getChildren();
7259         for (unsigned int i=0 ; i<children.size() ; i++)
7260             {
7261             Element *child = children[i];
7262             String tagName = child->getName();
7263             if (tagName == "fileset")
7264                 {
7265                 if (!parseFileSet(child, parent, fileSet))
7266                     return false;
7267                 }
7268             }
7269         return true;
7270         }
7272 private:
7274     String command;
7275     String fileName;
7276     FileSet fileSet;
7278 };
7283 /**
7284  * Strip an executable
7285  */
7286 class TaskStrip : public Task
7288 public:
7290     TaskStrip(MakeBase &par) : Task(par)
7291         { type = TASK_STRIP; name = "strip"; }
7293     virtual ~TaskStrip()
7294         {}
7296     virtual bool execute()
7297         {
7298         String fullName = parent.resolve(fileName);
7299         //trace("fullDir:%s", fullDir.c_str());
7300         String cmd;
7301         String outbuf, errbuf;
7303         if (symFileName.size()>0)
7304             {
7305             String symFullName = parent.resolve(symFileName);
7306             cmd = "objcopy --only-keep-debug ";
7307             cmd.append(getNativePath(fullName));
7308             cmd.append(" ");
7309             cmd.append(getNativePath(symFullName));
7310             if (!executeCommand(cmd, "", outbuf, errbuf))
7311                 {
7312                 error("<strip> symbol file failed : %s", errbuf.c_str());
7313                 return false;
7314                 }
7315             }
7316             
7317         cmd = "strip ";
7318         cmd.append(getNativePath(fullName));
7319         if (!executeCommand(cmd, "", outbuf, errbuf))
7320             {
7321             error("<strip> failed : %s", errbuf.c_str());
7322             return false;
7323             }
7324         return true;
7325         }
7327     virtual bool parse(Element *elem)
7328         {
7329         if (!parent.getAttribute(elem, "file", fileName))
7330             return false;
7331         if (!parent.getAttribute(elem, "symfile", symFileName))
7332             return false;
7333         if (fileName.size() == 0)
7334             {
7335             error("<strip> requires 'file=\"fileName\"' attribute");
7336             return false;
7337             }
7338         return true;
7339         }
7341 private:
7343     String fileName;
7344     String symFileName;
7345 };
7348 /**
7349  *
7350  */
7351 class TaskTouch : public Task
7353 public:
7355     TaskTouch(MakeBase &par) : Task(par)
7356         { type = TASK_TOUCH; name = "touch"; }
7358     virtual ~TaskTouch()
7359         {}
7361     virtual bool execute()
7362         {
7363         String fullName = parent.resolve(fileName);
7364         String nativeFile = getNativePath(fullName);
7365         if (!isRegularFile(fullName) && !isDirectory(fullName))
7366             {            
7367             // S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH
7368             int ret = creat(nativeFile.c_str(), 0666);
7369             if (ret != 0) 
7370                 {
7371                 error("<touch> could not create '%s' : %s",
7372                     nativeFile.c_str(), strerror(ret));
7373                 return false;
7374                 }
7375             return true;
7376             }
7377         int ret = utime(nativeFile.c_str(), (struct utimbuf *)0);
7378         if (ret != 0)
7379             {
7380             error("<touch> could not update the modification time for '%s' : %s",
7381                 nativeFile.c_str(), strerror(ret));
7382             return false;
7383             }
7384         return true;
7385         }
7387     virtual bool parse(Element *elem)
7388         {
7389         //trace("touch parse");
7390         if (!parent.getAttribute(elem, "file", fileName))
7391             return false;
7392         if (fileName.size() == 0)
7393             {
7394             error("<touch> requires 'file=\"fileName\"' attribute");
7395             return false;
7396             }
7397         return true;
7398         }
7400     String fileName;
7401 };
7404 /**
7405  *
7406  */
7407 class TaskTstamp : public Task
7409 public:
7411     TaskTstamp(MakeBase &par) : Task(par)
7412         { type = TASK_TSTAMP; name = "tstamp"; }
7414     virtual ~TaskTstamp()
7415         {}
7417     virtual bool execute()
7418         {
7419         return true;
7420         }
7422     virtual bool parse(Element *elem)
7423         {
7424         //trace("tstamp parse");
7425         return true;
7426         }
7427 };
7431 /**
7432  *
7433  */
7434 Task *Task::createTask(Element *elem, int lineNr)
7436     String tagName = elem->getName();
7437     //trace("task:%s", tagName.c_str());
7438     Task *task = NULL;
7439     if (tagName == "cc")
7440         task = new TaskCC(parent);
7441     else if (tagName == "copy")
7442         task = new TaskCopy(parent);
7443     else if (tagName == "delete")
7444         task = new TaskDelete(parent);
7445     else if (tagName == "jar")
7446         task = new TaskJar(parent);
7447     else if (tagName == "javac")
7448         task = new TaskJavac(parent);
7449     else if (tagName == "link")
7450         task = new TaskLink(parent);
7451     else if (tagName == "makefile")
7452         task = new TaskMakeFile(parent);
7453     else if (tagName == "mkdir")
7454         task = new TaskMkDir(parent);
7455     else if (tagName == "msgfmt")
7456         task = new TaskMsgFmt(parent);
7457     else if (tagName == "ranlib")
7458         task = new TaskRanlib(parent);
7459     else if (tagName == "rc")
7460         task = new TaskRC(parent);
7461     else if (tagName == "sharedlib")
7462         task = new TaskSharedLib(parent);
7463     else if (tagName == "staticlib")
7464         task = new TaskStaticLib(parent);
7465     else if (tagName == "strip")
7466         task = new TaskStrip(parent);
7467     else if (tagName == "touch")
7468         task = new TaskTouch(parent);
7469     else if (tagName == "tstamp")
7470         task = new TaskTstamp(parent);
7471     else
7472         {
7473         error("Unknown task '%s'", tagName.c_str());
7474         return NULL;
7475         }
7477     task->setLine(lineNr);
7479     if (!task->parse(elem))
7480         {
7481         delete task;
7482         return NULL;
7483         }
7484     return task;
7489 //########################################################################
7490 //# T A R G E T
7491 //########################################################################
7493 /**
7494  *
7495  */
7496 class Target : public MakeBase
7499 public:
7501     /**
7502      *
7503      */
7504     Target(Make &par) : parent(par)
7505         { init(); }
7507     /**
7508      *
7509      */
7510     Target(const Target &other) : parent(other.parent)
7511         { init(); assign(other); }
7513     /**
7514      *
7515      */
7516     Target &operator=(const Target &other)
7517         { init(); assign(other); return *this; }
7519     /**
7520      *
7521      */
7522     virtual ~Target()
7523         { cleanup() ; }
7526     /**
7527      *
7528      */
7529     virtual Make &getParent()
7530         { return parent; }
7532     /**
7533      *
7534      */
7535     virtual String getName()
7536         { return name; }
7538     /**
7539      *
7540      */
7541     virtual void setName(const String &val)
7542         { name = val; }
7544     /**
7545      *
7546      */
7547     virtual String getDescription()
7548         { return description; }
7550     /**
7551      *
7552      */
7553     virtual void setDescription(const String &val)
7554         { description = val; }
7556     /**
7557      *
7558      */
7559     virtual void addDependency(const String &val)
7560         { deps.push_back(val); }
7562     /**
7563      *
7564      */
7565     virtual void parseDependencies(const String &val)
7566         { deps = tokenize(val, ", "); }
7568     /**
7569      *
7570      */
7571     virtual std::vector<String> &getDependencies()
7572         { return deps; }
7574     /**
7575      *
7576      */
7577     virtual String getIf()
7578         { return ifVar; }
7580     /**
7581      *
7582      */
7583     virtual void setIf(const String &val)
7584         { ifVar = val; }
7586     /**
7587      *
7588      */
7589     virtual String getUnless()
7590         { return unlessVar; }
7592     /**
7593      *
7594      */
7595     virtual void setUnless(const String &val)
7596         { unlessVar = val; }
7598     /**
7599      *
7600      */
7601     virtual void addTask(Task *val)
7602         { tasks.push_back(val); }
7604     /**
7605      *
7606      */
7607     virtual std::vector<Task *> &getTasks()
7608         { return tasks; }
7610 private:
7612     void init()
7613         {
7614         }
7616     void cleanup()
7617         {
7618         tasks.clear();
7619         }
7621     void assign(const Target &other)
7622         {
7623         //parent      = other.parent;
7624         name        = other.name;
7625         description = other.description;
7626         ifVar       = other.ifVar;
7627         unlessVar   = other.unlessVar;
7628         deps        = other.deps;
7629         tasks       = other.tasks;
7630         }
7632     Make &parent;
7634     String name;
7636     String description;
7638     String ifVar;
7640     String unlessVar;
7642     std::vector<String> deps;
7644     std::vector<Task *> tasks;
7646 };
7655 //########################################################################
7656 //# M A K E
7657 //########################################################################
7660 /**
7661  *
7662  */
7663 class Make : public MakeBase
7666 public:
7668     /**
7669      *
7670      */
7671     Make()
7672         { init(); }
7674     /**
7675      *
7676      */
7677     Make(const Make &other)
7678         { assign(other); }
7680     /**
7681      *
7682      */
7683     Make &operator=(const Make &other)
7684         { assign(other); return *this; }
7686     /**
7687      *
7688      */
7689     virtual ~Make()
7690         { cleanup(); }
7692     /**
7693      *
7694      */
7695     virtual std::map<String, Target> &getTargets()
7696         { return targets; }
7699     /**
7700      *
7701      */
7702     virtual String version()
7703         { return BUILDTOOL_VERSION; }
7705     /**
7706      * Overload a <property>
7707      */
7708     virtual bool specifyProperty(const String &name,
7709                                  const String &value);
7711     /**
7712      *
7713      */
7714     virtual bool run();
7716     /**
7717      *
7718      */
7719     virtual bool run(const String &target);
7723 private:
7725     /**
7726      *
7727      */
7728     void init();
7730     /**
7731      *
7732      */
7733     void cleanup();
7735     /**
7736      *
7737      */
7738     void assign(const Make &other);
7740     /**
7741      *
7742      */
7743     bool executeTask(Task &task);
7746     /**
7747      *
7748      */
7749     bool executeTarget(Target &target,
7750              std::set<String> &targetsCompleted);
7753     /**
7754      *
7755      */
7756     bool execute();
7758     /**
7759      *
7760      */
7761     bool checkTargetDependencies(Target &prop,
7762                     std::vector<String> &depList);
7764     /**
7765      *
7766      */
7767     bool parsePropertyFile(const String &fileName,
7768                            const String &prefix);
7770     /**
7771      *
7772      */
7773     bool parseProperty(Element *elem);
7775     /**
7776      *
7777      */
7778     bool parseFile();
7780     /**
7781      *
7782      */
7783     std::vector<String> glob(const String &pattern);
7786     //###############
7787     //# Fields
7788     //###############
7790     String projectName;
7792     String currentTarget;
7794     String defaultTarget;
7796     String specifiedTarget;
7798     String baseDir;
7800     String description;
7801     
7802     String envAlias;
7804     //std::vector<Property> properties;
7805     
7806     std::map<String, Target> targets;
7808     std::vector<Task *> allTasks;
7809     
7810     std::map<String, String> specifiedProperties;
7812 };
7815 //########################################################################
7816 //# C L A S S  M A I N T E N A N C E
7817 //########################################################################
7819 /**
7820  *
7821  */
7822 void Make::init()
7824     uri             = "build.xml";
7825     projectName     = "";
7826     currentTarget   = "";
7827     defaultTarget   = "";
7828     specifiedTarget = "";
7829     baseDir         = "";
7830     description     = "";
7831     envAlias        = "";
7832     properties.clear();
7833     for (unsigned int i = 0 ; i < allTasks.size() ; i++)
7834         delete allTasks[i];
7835     allTasks.clear();
7840 /**
7841  *
7842  */
7843 void Make::cleanup()
7845     for (unsigned int i = 0 ; i < allTasks.size() ; i++)
7846         delete allTasks[i];
7847     allTasks.clear();
7852 /**
7853  *
7854  */
7855 void Make::assign(const Make &other)
7857     uri              = other.uri;
7858     projectName      = other.projectName;
7859     currentTarget    = other.currentTarget;
7860     defaultTarget    = other.defaultTarget;
7861     specifiedTarget  = other.specifiedTarget;
7862     baseDir          = other.baseDir;
7863     description      = other.description;
7864     properties       = other.properties;
7869 //########################################################################
7870 //# U T I L I T Y    T A S K S
7871 //########################################################################
7873 /**
7874  *  Perform a file globbing
7875  */
7876 std::vector<String> Make::glob(const String &pattern)
7878     std::vector<String> res;
7879     return res;
7883 //########################################################################
7884 //# P U B L I C    A P I
7885 //########################################################################
7889 /**
7890  *
7891  */
7892 bool Make::executeTarget(Target &target,
7893              std::set<String> &targetsCompleted)
7896     String name = target.getName();
7898     //First get any dependencies for this target
7899     std::vector<String> deps = target.getDependencies();
7900     for (unsigned int i=0 ; i<deps.size() ; i++)
7901         {
7902         String dep = deps[i];
7903         //Did we do it already?  Skip
7904         if (targetsCompleted.find(dep)!=targetsCompleted.end())
7905             continue;
7906             
7907         std::map<String, Target> &tgts =
7908                target.getParent().getTargets();
7909         std::map<String, Target>::iterator iter =
7910                tgts.find(dep);
7911         if (iter == tgts.end())
7912             {
7913             error("Target '%s' dependency '%s' not found",
7914                       name.c_str(),  dep.c_str());
7915             return false;
7916             }
7917         Target depTarget = iter->second;
7918         if (!executeTarget(depTarget, targetsCompleted))
7919             {
7920             return false;
7921             }
7922         }
7924     status("## Target : %s", name.c_str());
7926     //Now let's do the tasks
7927     std::vector<Task *> &tasks = target.getTasks();
7928     for (unsigned int i=0 ; i<tasks.size() ; i++)
7929         {
7930         Task *task = tasks[i];
7931         status("---- task : %s", task->getName().c_str());
7932         if (!task->execute())
7933             {
7934             return false;
7935             }
7936         }
7937         
7938     targetsCompleted.insert(name);
7939     
7940     return true;
7945 /**
7946  *  Main execute() method.  Start here and work
7947  *  up the dependency tree 
7948  */
7949 bool Make::execute()
7951     status("######## EXECUTE");
7953     //Determine initial target
7954     if (specifiedTarget.size()>0)
7955         {
7956         currentTarget = specifiedTarget;
7957         }
7958     else if (defaultTarget.size()>0)
7959         {
7960         currentTarget = defaultTarget;
7961         }
7962     else
7963         {
7964         error("execute: no specified or default target requested");
7965         return false;
7966         }
7968     std::map<String, Target>::iterator iter =
7969                targets.find(currentTarget);
7970     if (iter == targets.end())
7971         {
7972         error("Initial target '%s' not found",
7973                  currentTarget.c_str());
7974         return false;
7975         }
7976         
7977     //Now run
7978     Target target = iter->second;
7979     std::set<String> targetsCompleted;
7980     if (!executeTarget(target, targetsCompleted))
7981         {
7982         return false;
7983         }
7985     status("######## EXECUTE COMPLETE");
7986     return true;
7992 /**
7993  *
7994  */
7995 bool Make::checkTargetDependencies(Target &target, 
7996                             std::vector<String> &depList)
7998     String tgtName = target.getName().c_str();
7999     depList.push_back(tgtName);
8001     std::vector<String> deps = target.getDependencies();
8002     for (unsigned int i=0 ; i<deps.size() ; i++)
8003         {
8004         String dep = deps[i];
8005         //First thing entered was the starting Target
8006         if (dep == depList[0])
8007             {
8008             error("Circular dependency '%s' found at '%s'",
8009                       dep.c_str(), tgtName.c_str());
8010             std::vector<String>::iterator diter;
8011             for (diter=depList.begin() ; diter!=depList.end() ; diter++)
8012                 {
8013                 error("  %s", diter->c_str());
8014                 }
8015             return false;
8016             }
8018         std::map<String, Target> &tgts =
8019                   target.getParent().getTargets();
8020         std::map<String, Target>::iterator titer = tgts.find(dep);
8021         if (titer == tgts.end())
8022             {
8023             error("Target '%s' dependency '%s' not found",
8024                       tgtName.c_str(), dep.c_str());
8025             return false;
8026             }
8027         if (!checkTargetDependencies(titer->second, depList))
8028             {
8029             return false;
8030             }
8031         }
8032     return true;
8039 static int getword(int pos, const String &inbuf, String &result)
8041     int p = pos;
8042     int len = (int)inbuf.size();
8043     String val;
8044     while (p < len)
8045         {
8046         char ch = inbuf[p];
8047         if (!isalnum(ch) && ch!='.' && ch!='_')
8048             break;
8049         val.push_back(ch);
8050         p++;
8051         }
8052     result = val;
8053     return p;
8059 /**
8060  *
8061  */
8062 bool Make::parsePropertyFile(const String &fileName,
8063                              const String &prefix)
8065     FILE *f = fopen(fileName.c_str(), "r");
8066     if (!f)
8067         {
8068         error("could not open property file %s", fileName.c_str());
8069         return false;
8070         }
8071     int linenr = 0;
8072     while (!feof(f))
8073         {
8074         char buf[256];
8075         if (!fgets(buf, 255, f))
8076             break;
8077         linenr++;
8078         String s = buf;
8079         s = trim(s);
8080         int len = s.size();
8081         if (len == 0)
8082             continue;
8083         if (s[0] == '#')
8084             continue;
8085         String key;
8086         String val;
8087         int p = 0;
8088         int p2 = getword(p, s, key);
8089         if (p2 <= p)
8090             {
8091             error("property file %s, line %d: expected keyword",
8092                     fileName.c_str(), linenr);
8093             return false;
8094             }
8095         if (prefix.size() > 0)
8096             {
8097             key.insert(0, prefix);
8098             }
8100         //skip whitespace
8101         for (p=p2 ; p<len ; p++)
8102             if (!isspace(s[p]))
8103                 break;
8105         if (p>=len || s[p]!='=')
8106             {
8107             error("property file %s, line %d: expected '='",
8108                     fileName.c_str(), linenr);
8109             return false;
8110             }
8111         p++;
8113         //skip whitespace
8114         for ( ; p<len ; p++)
8115             if (!isspace(s[p]))
8116                 break;
8118         /* This way expects a word after the =
8119         p2 = getword(p, s, val);
8120         if (p2 <= p)
8121             {
8122             error("property file %s, line %d: expected value",
8123                     fileName.c_str(), linenr);
8124             return false;
8125             }
8126         */
8127         // This way gets the rest of the line after the =
8128         if (p>=len)
8129             {
8130             error("property file %s, line %d: expected value",
8131                     fileName.c_str(), linenr);
8132             return false;
8133             }
8134         val = s.substr(p);
8135         if (key.size()==0)
8136             continue;
8137         //allow property to be set, even if val=""
8139         //trace("key:'%s' val:'%s'", key.c_str(), val.c_str());
8140         //See if we wanted to overload this property
8141         std::map<String, String>::iterator iter =
8142             specifiedProperties.find(key);
8143         if (iter!=specifiedProperties.end())
8144             {
8145             val = iter->second;
8146             status("overloading property '%s' = '%s'",
8147                    key.c_str(), val.c_str());
8148             }
8149         properties[key] = val;
8150         }
8151     fclose(f);
8152     return true;
8158 /**
8159  *
8160  */
8161 bool Make::parseProperty(Element *elem)
8163     std::vector<Attribute> &attrs = elem->getAttributes();
8164     for (unsigned int i=0 ; i<attrs.size() ; i++)
8165         {
8166         String attrName = attrs[i].getName();
8167         String attrVal  = attrs[i].getValue();
8169         if (attrName == "name")
8170             {
8171             String val;
8172             if (!getAttribute(elem, "value", val))
8173                 return false;
8174             if (val.size() > 0)
8175                 {
8176                 properties[attrVal] = val;
8177                 }
8178             else
8179                 {
8180                 if (!getAttribute(elem, "location", val))
8181                     return false;
8182                 //let the property exist, even if not defined
8183                 properties[attrVal] = val;
8184                 }
8185             //See if we wanted to overload this property
8186             std::map<String, String>::iterator iter =
8187                 specifiedProperties.find(attrVal);
8188             if (iter != specifiedProperties.end())
8189                 {
8190                 val = iter->second;
8191                 status("overloading property '%s' = '%s'",
8192                     attrVal.c_str(), val.c_str());
8193                 properties[attrVal] = val;
8194                 }
8195             }
8196         else if (attrName == "file")
8197             {
8198             String prefix;
8199             if (!getAttribute(elem, "prefix", prefix))
8200                 return false;
8201             if (prefix.size() > 0)
8202                 {
8203                 if (prefix[prefix.size()-1] != '.')
8204                     prefix.push_back('.');
8205                 }
8206             if (!parsePropertyFile(attrName, prefix))
8207                 return false;
8208             }
8209         else if (attrName == "environment")
8210             {
8211             if (envAlias.size() > 0)
8212                 {
8213                 error("environment property can only be set once");
8214                 return false;
8215                 }
8216             envAlias = attrVal;
8217             }
8218         }
8220     return true;
8226 /**
8227  *
8228  */
8229 bool Make::parseFile()
8231     status("######## PARSE : %s", uri.getPath().c_str());
8233     setLine(0);
8235     Parser parser;
8236     Element *root = parser.parseFile(uri.getNativePath());
8237     if (!root)
8238         {
8239         error("Could not open %s for reading",
8240               uri.getNativePath().c_str());
8241         return false;
8242         }
8243     
8244     setLine(root->getLine());
8246     if (root->getChildren().size()==0 ||
8247         root->getChildren()[0]->getName()!="project")
8248         {
8249         error("Main xml element should be <project>");
8250         delete root;
8251         return false;
8252         }
8254     //########## Project attributes
8255     Element *project = root->getChildren()[0];
8256     String s = project->getAttribute("name");
8257     if (s.size() > 0)
8258         projectName = s;
8259     s = project->getAttribute("default");
8260     if (s.size() > 0)
8261         defaultTarget = s;
8262     s = project->getAttribute("basedir");
8263     if (s.size() > 0)
8264         baseDir = s;
8266     //######### PARSE MEMBERS
8267     std::vector<Element *> children = project->getChildren();
8268     for (unsigned int i=0 ; i<children.size() ; i++)
8269         {
8270         Element *elem = children[i];
8271         setLine(elem->getLine());
8272         String tagName = elem->getName();
8274         //########## DESCRIPTION
8275         if (tagName == "description")
8276             {
8277             description = parser.trim(elem->getValue());
8278             }
8280         //######### PROPERTY
8281         else if (tagName == "property")
8282             {
8283             if (!parseProperty(elem))
8284                 return false;
8285             }
8287         //######### TARGET
8288         else if (tagName == "target")
8289             {
8290             String tname   = elem->getAttribute("name");
8291             String tdesc   = elem->getAttribute("description");
8292             String tdeps   = elem->getAttribute("depends");
8293             String tif     = elem->getAttribute("if");
8294             String tunless = elem->getAttribute("unless");
8295             Target target(*this);
8296             target.setName(tname);
8297             target.setDescription(tdesc);
8298             target.parseDependencies(tdeps);
8299             target.setIf(tif);
8300             target.setUnless(tunless);
8301             std::vector<Element *> telems = elem->getChildren();
8302             for (unsigned int i=0 ; i<telems.size() ; i++)
8303                 {
8304                 Element *telem = telems[i];
8305                 Task breeder(*this);
8306                 Task *task = breeder.createTask(telem, telem->getLine());
8307                 if (!task)
8308                     return false;
8309                 allTasks.push_back(task);
8310                 target.addTask(task);
8311                 }
8313             //Check name
8314             if (tname.size() == 0)
8315                 {
8316                 error("no name for target");
8317                 return false;
8318                 }
8319             //Check for duplicate name
8320             if (targets.find(tname) != targets.end())
8321                 {
8322                 error("target '%s' already defined", tname.c_str());
8323                 return false;
8324                 }
8325             //more work than targets[tname]=target, but avoids default allocator
8326             targets.insert(std::make_pair<String, Target>(tname, target));
8327             }
8328         //######### none of the above
8329         else
8330             {
8331             error("unknown toplevel tag: <%s>", tagName.c_str());
8332             return false;
8333             }
8335         }
8337     std::map<String, Target>::iterator iter;
8338     for (iter = targets.begin() ; iter!= targets.end() ; iter++)
8339         {
8340         Target tgt = iter->second;
8341         std::vector<String> depList;
8342         if (!checkTargetDependencies(tgt, depList))
8343             {
8344             return false;
8345             }
8346         }
8349     delete root;
8350     status("######## PARSE COMPLETE");
8351     return true;
8355 /**
8356  * Overload a <property>
8357  */
8358 bool Make::specifyProperty(const String &name, const String &value)
8360     if (specifiedProperties.find(name) != specifiedProperties.end())
8361         {
8362         error("Property %s already specified", name.c_str());
8363         return false;
8364         }
8365     specifiedProperties[name] = value;
8366     return true;
8371 /**
8372  *
8373  */
8374 bool Make::run()
8376     if (!parseFile())
8377         return false;
8378         
8379     if (!execute())
8380         return false;
8382     return true;
8388 /**
8389  * Get a formatted MM:SS.sss time elapsed string
8390  */ 
8391 static String
8392 timeDiffString(struct timeval &x, struct timeval &y)
8394     long microsX  = x.tv_usec;
8395     long secondsX = x.tv_sec;
8396     long microsY  = y.tv_usec;
8397     long secondsY = y.tv_sec;
8398     if (microsX < microsY)
8399         {
8400         microsX += 1000000;
8401         secondsX -= 1;
8402         }
8404     int seconds = (int)(secondsX - secondsY);
8405     int millis  = (int)((microsX - microsY)/1000);
8407     int minutes = seconds/60;
8408     seconds -= minutes*60;
8409     char buf[80];
8410     snprintf(buf, 79, "%dm %d.%03ds", minutes, seconds, millis);
8411     String ret = buf;
8412     return ret;
8413     
8416 /**
8417  *
8418  */
8419 bool Make::run(const String &target)
8421     status("####################################################");
8422     status("#   %s", version().c_str());
8423     status("####################################################");
8424     struct timeval timeStart, timeEnd;
8425     ::gettimeofday(&timeStart, NULL);
8426     specifiedTarget = target;
8427     if (!run())
8428         return false;
8429     ::gettimeofday(&timeEnd, NULL);
8430     String timeStr = timeDiffString(timeEnd, timeStart);
8431     status("####################################################");
8432     status("#   BuildTool Completed : %s", timeStr.c_str());
8433     status("####################################################");
8434     return true;
8443 }// namespace buildtool
8444 //########################################################################
8445 //# M A I N
8446 //########################################################################
8448 typedef buildtool::String String;
8450 /**
8451  *  Format an error message in printf() style
8452  */
8453 static void error(char *fmt, ...)
8455     va_list ap;
8456     va_start(ap, fmt);
8457     fprintf(stderr, "BuildTool error: ");
8458     vfprintf(stderr, fmt, ap);
8459     fprintf(stderr, "\n");
8460     va_end(ap);
8464 static bool parseProperty(const String &s, String &name, String &val)
8466     int len = s.size();
8467     int i;
8468     for (i=0 ; i<len ; i++)
8469         {
8470         char ch = s[i];
8471         if (ch == '=')
8472             break;
8473         name.push_back(ch);
8474         }
8475     if (i>=len || s[i]!='=')
8476         {
8477         error("property requires -Dname=value");
8478         return false;
8479         }
8480     i++;
8481     for ( ; i<len ; i++)
8482         {
8483         char ch = s[i];
8484         val.push_back(ch);
8485         }
8486     return true;
8490 /**
8491  * Compare a buffer with a key, for the length of the key
8492  */
8493 static bool sequ(const String &buf, char *key)
8495     int len = buf.size();
8496     for (int i=0 ; key[i] && i<len ; i++)
8497         {
8498         if (key[i] != buf[i])
8499             return false;
8500         }        
8501     return true;
8504 static void usage(int argc, char **argv)
8506     printf("usage:\n");
8507     printf("   %s [options] [target]\n", argv[0]);
8508     printf("Options:\n");
8509     printf("  -help, -h              print this message\n");
8510     printf("  -version               print the version information and exit\n");
8511     printf("  -file <file>           use given buildfile\n");
8512     printf("  -f <file>                 ''\n");
8513     printf("  -D<property>=<value>   use value for given property\n");
8519 /**
8520  * Parse the command-line args, get our options,
8521  * and run this thing
8522  */   
8523 static bool parseOptions(int argc, char **argv)
8525     if (argc < 1)
8526         {
8527         error("Cannot parse arguments");
8528         return false;
8529         }
8531     buildtool::Make make;
8533     String target;
8535     //char *progName = argv[0];
8536     for (int i=1 ; i<argc ; i++)
8537         {
8538         String arg = argv[i];
8539         if (arg.size()>1 && arg[0]=='-')
8540             {
8541             if (arg == "-h" || arg == "-help")
8542                 {
8543                 usage(argc,argv);
8544                 return true;
8545                 }
8546             else if (arg == "-version")
8547                 {
8548                 printf("%s", make.version().c_str());
8549                 return true;
8550                 }
8551             else if (arg == "-f" || arg == "-file")
8552                 {
8553                 if (i>=argc)
8554                    {
8555                    usage(argc, argv);
8556                    return false;
8557                    }
8558                 i++; //eat option
8559                 make.setURI(argv[i]);
8560                 }
8561             else if (arg.size()>2 && sequ(arg, "-D"))
8562                 {
8563                 String s = arg.substr(2, s.size());
8564                 String name, value;
8565                 if (!parseProperty(s, name, value))
8566                    {
8567                    usage(argc, argv);
8568                    return false;
8569                    }
8570                 if (!make.specifyProperty(name, value))
8571                     return false;
8572                 }
8573             else
8574                 {
8575                 error("Unknown option:%s", arg.c_str());
8576                 return false;
8577                 }
8578             }
8579         else
8580             {
8581             if (target.size()>0)
8582                 {
8583                 error("only one initial target");
8584                 usage(argc, argv);
8585                 return false;
8586                 }
8587             target = arg;
8588             }
8589         }
8591     //We have the options.  Now execute them
8592     if (!make.run(target))
8593         return false;
8595     return true;
8601 /*
8602 static bool runMake()
8604     buildtool::Make make;
8605     if (!make.run())
8606         return false;
8607     return true;
8611 static bool pkgConfigTest()
8613     buildtool::PkgConfig pkgConfig;
8614     if (!pkgConfig.readFile("gtk+-2.0.pc"))
8615         return false;
8616     return true;
8621 static bool depTest()
8623     buildtool::DepTool deptool;
8624     deptool.setSourceDirectory("/dev/ink/inkscape/src");
8625     if (!deptool.generateDependencies("build.dep"))
8626         return false;
8627     std::vector<buildtool::DepRec> res =
8628            deptool.loadDepFile("build.dep");
8629     if (res.size() == 0)
8630         return false;
8631     return true;
8634 static bool popenTest()
8636     buildtool::Make make;
8637     buildtool::String out, err;
8638     bool ret = make.executeCommand("gcc xx.cpp", "", out, err);
8639     printf("Popen test:%d '%s' '%s'\n", ret, out.c_str(), err.c_str());
8640     return true;
8644 static bool propFileTest()
8646     buildtool::Make make;
8647     make.parsePropertyFile("test.prop", "test.");
8648     return true;
8650 */
8652 int main(int argc, char **argv)
8655     if (!parseOptions(argc, argv))
8656         return 1;
8657     /*
8658     if (!popenTest())
8659         return 1;
8661     if (!depTest())
8662         return 1;
8663     if (!propFileTest())
8664         return 1;
8665     if (runMake())
8666         return 1;
8667     */
8668     return 0;
8672 //########################################################################
8673 //# E N D 
8674 //########################################################################