Code

More cleanup of piping. Add ccompiler listing.
[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.4, 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         parent = NULL;
964         }
966     Element(const String &nameArg)
967         {
968         parent = NULL;
969         name   = nameArg;
970         }
972     Element(const String &nameArg, const String &valueArg)
973         {
974         parent = NULL;
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();
1043 protected:
1045     void assign(const Element &other)
1046         {
1047         parent     = other.parent;
1048         children   = other.children;
1049         attributes = other.attributes;
1050         namespaces = other.namespaces;
1051         name       = other.name;
1052         value      = other.value;
1053         }
1055     void findElementsRecursive(std::vector<Element *>&res, const String &name);
1057     void writeIndentedRecursive(FILE *f, int indent);
1059     Element *parent;
1061     std::vector<Element *>children;
1063     std::vector<Attribute> attributes;
1064     std::vector<Namespace> namespaces;
1066     String name;
1067     String value;
1069 };
1075 class Parser
1077 public:
1078     /**
1079      * Constructor
1080      */
1081     Parser()
1082         { init(); }
1084     virtual ~Parser()
1085         {}
1087     /**
1088      * Parse XML in a char buffer.
1089      * @param buf a character buffer to parse
1090      * @param pos position to start parsing
1091      * @param len number of chars, from pos, to parse.
1092      * @return a pointer to the root of the XML document;
1093      */
1094     Element *parse(const char *buf,int pos,int len);
1096     /**
1097      * Parse XML in a char buffer.
1098      * @param buf a character buffer to parse
1099      * @param pos position to start parsing
1100      * @param len number of chars, from pos, to parse.
1101      * @return a pointer to the root of the XML document;
1102      */
1103     Element *parse(const String &buf);
1105     /**
1106      * Parse a named XML file.  The file is loaded like a data file;
1107      * the original format is not preserved.
1108      * @param fileName the name of the file to read
1109      * @return a pointer to the root of the XML document;
1110      */
1111     Element *parseFile(const String &fileName);
1113     /**
1114      * Utility method to preprocess a string for XML
1115      * output, escaping its entities.
1116      * @param str the string to encode
1117      */
1118     static String encode(const String &str);
1120     /**
1121      *  Removes whitespace from beginning and end of a string
1122      */
1123     String trim(const String &s);
1125 private:
1127     void init()
1128         {
1129         keepGoing       = true;
1130         currentNode     = NULL;
1131         parselen        = 0;
1132         parsebuf        = NULL;
1133         currentPosition = 0;
1134         }
1136     void getLineAndColumn(long pos, long *lineNr, long *colNr);
1138     void error(char *fmt, ...);
1140     int peek(long pos);
1142     int match(long pos, const char *text);
1144     int skipwhite(long p);
1146     int getWord(int p0, String &buf);
1148     int getQuoted(int p0, String &buf, int do_i_parse);
1150     int parseVersion(int p0);
1152     int parseDoctype(int p0);
1154     int parseElement(int p0, Element *par,int depth);
1156     Element *parse(XMLCh *buf,int pos,int len);
1158     bool       keepGoing;
1159     Element    *currentNode;
1160     long       parselen;
1161     XMLCh      *parsebuf;
1162     String  cdatabuf;
1163     long       currentPosition;
1164     int        colNr;
1166 };
1171 //########################################################################
1172 //# E L E M E N T
1173 //########################################################################
1175 Element *Element::clone()
1177     Element *elem = new Element(name, value);
1178     elem->parent     = parent;
1179     elem->attributes = attributes;
1180     elem->namespaces = namespaces;
1182     std::vector<Element *>::iterator iter;
1183     for (iter = children.begin(); iter != children.end() ; iter++)
1184         {
1185         elem->addChild((*iter)->clone());
1186         }
1187     return elem;
1191 void Element::findElementsRecursive(std::vector<Element *>&res, const String &name)
1193     if (getName() == name)
1194         {
1195         res.push_back(this);
1196         }
1197     for (unsigned int i=0; i<children.size() ; i++)
1198         children[i]->findElementsRecursive(res, name);
1201 std::vector<Element *> Element::findElements(const String &name)
1203     std::vector<Element *> res;
1204     findElementsRecursive(res, name);
1205     return res;
1208 String Element::getAttribute(const String &name)
1210     for (unsigned int i=0 ; i<attributes.size() ; i++)
1211         if (attributes[i].getName() ==name)
1212             return attributes[i].getValue();
1213     return "";
1216 String Element::getTagAttribute(const String &tagName, const String &attrName)
1218     std::vector<Element *>elems = findElements(tagName);
1219     if (elems.size() <1)
1220         return "";
1221     String res = elems[0]->getAttribute(attrName);
1222     return res;
1225 String Element::getTagValue(const String &tagName)
1227     std::vector<Element *>elems = findElements(tagName);
1228     if (elems.size() <1)
1229         return "";
1230     String res = elems[0]->getValue();
1231     return res;
1234 void Element::addChild(Element *child)
1236     if (!child)
1237         return;
1238     child->parent = this;
1239     children.push_back(child);
1243 void Element::addAttribute(const String &name, const String &value)
1245     Attribute attr(name, value);
1246     attributes.push_back(attr);
1249 void Element::addNamespace(const String &prefix, const String &namespaceURI)
1251     Namespace ns(prefix, namespaceURI);
1252     namespaces.push_back(ns);
1255 void Element::writeIndentedRecursive(FILE *f, int indent)
1257     int i;
1258     if (!f)
1259         return;
1260     //Opening tag, and attributes
1261     for (i=0;i<indent;i++)
1262         fputc(' ',f);
1263     fprintf(f,"<%s",name.c_str());
1264     for (unsigned int i=0 ; i<attributes.size() ; i++)
1265         {
1266         fprintf(f," %s=\"%s\"",
1267               attributes[i].getName().c_str(),
1268               attributes[i].getValue().c_str());
1269         }
1270     for (unsigned int i=0 ; i<namespaces.size() ; i++)
1271         {
1272         fprintf(f," xmlns:%s=\"%s\"",
1273               namespaces[i].getPrefix().c_str(),
1274               namespaces[i].getNamespaceURI().c_str());
1275         }
1276     fprintf(f,">\n");
1278     //Between the tags
1279     if (value.size() > 0)
1280         {
1281         for (int i=0;i<indent;i++)
1282             fputc(' ', f);
1283         fprintf(f," %s\n", value.c_str());
1284         }
1286     for (unsigned int i=0 ; i<children.size() ; i++)
1287         children[i]->writeIndentedRecursive(f, indent+2);
1289     //Closing tag
1290     for (int i=0; i<indent; i++)
1291         fputc(' ',f);
1292     fprintf(f,"</%s>\n", name.c_str());
1295 void Element::writeIndented(FILE *f)
1297     writeIndentedRecursive(f, 0);
1300 void Element::print()
1302     writeIndented(stdout);
1306 //########################################################################
1307 //# P A R S E R
1308 //########################################################################
1312 typedef struct
1313     {
1314     char *escaped;
1315     char value;
1316     } EntityEntry;
1318 static EntityEntry entities[] =
1320     { "&amp;" , '&'  },
1321     { "&lt;"  , '<'  },
1322     { "&gt;"  , '>'  },
1323     { "&apos;", '\'' },
1324     { "&quot;", '"'  },
1325     { NULL    , '\0' }
1326 };
1330 /**
1331  *  Removes whitespace from beginning and end of a string
1332  */
1333 String Parser::trim(const String &s)
1335     if (s.size() < 1)
1336         return s;
1337     
1338     //Find first non-ws char
1339     unsigned int begin = 0;
1340     for ( ; begin < s.size() ; begin++)
1341         {
1342         if (!isspace(s[begin]))
1343             break;
1344         }
1346     //Find first non-ws char, going in reverse
1347     unsigned int end = s.size() - 1;
1348     for ( ; end > begin ; end--)
1349         {
1350         if (!isspace(s[end]))
1351             break;
1352         }
1353     //trace("begin:%d  end:%d", begin, end);
1355     String res = s.substr(begin, end-begin+1);
1356     return res;
1359 void Parser::getLineAndColumn(long pos, long *lineNr, long *colNr)
1361     long line = 1;
1362     long col  = 1;
1363     for (long i=0 ; i<pos ; i++)
1364         {
1365         XMLCh ch = parsebuf[i];
1366         if (ch == '\n' || ch == '\r')
1367             {
1368             col = 0;
1369             line ++;
1370             }
1371         else
1372             col++;
1373         }
1374     *lineNr = line;
1375     *colNr  = col;
1380 void Parser::error(char *fmt, ...)
1382     long lineNr;
1383     long colNr;
1384     getLineAndColumn(currentPosition, &lineNr, &colNr);
1385     va_list args;
1386     fprintf(stderr, "xml error at line %ld, column %ld:", lineNr, colNr);
1387     va_start(args,fmt);
1388     vfprintf(stderr,fmt,args);
1389     va_end(args) ;
1390     fprintf(stderr, "\n");
1395 int Parser::peek(long pos)
1397     if (pos >= parselen)
1398         return -1;
1399     currentPosition = pos;
1400     int ch = parsebuf[pos];
1401     //printf("ch:%c\n", ch);
1402     return ch;
1407 String Parser::encode(const String &str)
1409     String ret;
1410     for (unsigned int i=0 ; i<str.size() ; i++)
1411         {
1412         XMLCh ch = (XMLCh)str[i];
1413         if (ch == '&')
1414             ret.append("&amp;");
1415         else if (ch == '<')
1416             ret.append("&lt;");
1417         else if (ch == '>')
1418             ret.append("&gt;");
1419         else if (ch == '\'')
1420             ret.append("&apos;");
1421         else if (ch == '"')
1422             ret.append("&quot;");
1423         else
1424             ret.push_back(ch);
1426         }
1427     return ret;
1431 int Parser::match(long p0, const char *text)
1433     int p = p0;
1434     while (*text)
1435         {
1436         if (peek(p) != *text)
1437             return p0;
1438         p++; text++;
1439         }
1440     return p;
1445 int Parser::skipwhite(long p)
1448     while (p<parselen)
1449         {
1450         int p2 = match(p, "<!--");
1451         if (p2 > p)
1452             {
1453             p = p2;
1454             while (p<parselen)
1455               {
1456               p2 = match(p, "-->");
1457               if (p2 > p)
1458                   {
1459                   p = p2;
1460                   break;
1461                   }
1462               p++;
1463               }
1464           }
1465       XMLCh b = peek(p);
1466       if (!isspace(b))
1467           break;
1468       p++;
1469       }
1470   return p;
1473 /* modify this to allow all chars for an element or attribute name*/
1474 int Parser::getWord(int p0, String &buf)
1476     int p = p0;
1477     while (p<parselen)
1478         {
1479         XMLCh b = peek(p);
1480         if (b<=' ' || b=='/' || b=='>' || b=='=')
1481             break;
1482         buf.push_back(b);
1483         p++;
1484         }
1485     return p;
1488 int Parser::getQuoted(int p0, String &buf, int do_i_parse)
1491     int p = p0;
1492     if (peek(p) != '"' && peek(p) != '\'')
1493         return p0;
1494     p++;
1496     while ( p<parselen )
1497         {
1498         XMLCh b = peek(p);
1499         if (b=='"' || b=='\'')
1500             break;
1501         if (b=='&' && do_i_parse)
1502             {
1503             bool found = false;
1504             for (EntityEntry *ee = entities ; ee->value ; ee++)
1505                 {
1506                 int p2 = match(p, ee->escaped);
1507                 if (p2>p)
1508                     {
1509                     buf.push_back(ee->value);
1510                     p = p2;
1511                     found = true;
1512                     break;
1513                     }
1514                 }
1515             if (!found)
1516                 {
1517                 error("unterminated entity");
1518                 return false;
1519                 }
1520             }
1521         else
1522             {
1523             buf.push_back(b);
1524             p++;
1525             }
1526         }
1527     return p;
1530 int Parser::parseVersion(int p0)
1532     //printf("### parseVersion: %d\n", p0);
1534     int p = p0;
1536     p = skipwhite(p0);
1538     if (peek(p) != '<')
1539         return p0;
1541     p++;
1542     if (p>=parselen || peek(p)!='?')
1543         return p0;
1545     p++;
1547     String buf;
1549     while (p<parselen)
1550         {
1551         XMLCh ch = peek(p);
1552         if (ch=='?')
1553             {
1554             p++;
1555             break;
1556             }
1557         buf.push_back(ch);
1558         p++;
1559         }
1561     if (peek(p) != '>')
1562         return p0;
1563     p++;
1565     //printf("Got version:%s\n",buf.c_str());
1566     return p;
1569 int Parser::parseDoctype(int p0)
1571     //printf("### parseDoctype: %d\n", p0);
1573     int p = p0;
1574     p = skipwhite(p);
1576     if (p>=parselen || peek(p)!='<')
1577         return p0;
1579     p++;
1581     if (peek(p)!='!' || peek(p+1)=='-')
1582         return p0;
1583     p++;
1585     String buf;
1586     while (p<parselen)
1587         {
1588         XMLCh ch = peek(p);
1589         if (ch=='>')
1590             {
1591             p++;
1592             break;
1593             }
1594         buf.push_back(ch);
1595         p++;
1596         }
1598     //printf("Got doctype:%s\n",buf.c_str());
1599     return p;
1602 int Parser::parseElement(int p0, Element *par,int depth)
1605     int p = p0;
1607     int p2 = p;
1609     p = skipwhite(p);
1611     //## Get open tag
1612     XMLCh ch = peek(p);
1613     if (ch!='<')
1614         return p0;
1616     p++;
1618     String openTagName;
1619     p = skipwhite(p);
1620     p = getWord(p, openTagName);
1621     //printf("####tag :%s\n", openTagName.c_str());
1622     p = skipwhite(p);
1624     //Add element to tree
1625     Element *n = new Element(openTagName);
1626     n->parent = par;
1627     par->addChild(n);
1629     // Get attributes
1630     if (peek(p) != '>')
1631         {
1632         while (p<parselen)
1633             {
1634             p = skipwhite(p);
1635             ch = peek(p);
1636             //printf("ch:%c\n",ch);
1637             if (ch=='>')
1638                 break;
1639             else if (ch=='/' && p<parselen+1)
1640                 {
1641                 p++;
1642                 p = skipwhite(p);
1643                 ch = peek(p);
1644                 if (ch=='>')
1645                     {
1646                     p++;
1647                     //printf("quick close\n");
1648                     return p;
1649                     }
1650                 }
1651             String attrName;
1652             p2 = getWord(p, attrName);
1653             if (p2==p)
1654                 break;
1655             //printf("name:%s",buf);
1656             p=p2;
1657             p = skipwhite(p);
1658             ch = peek(p);
1659             //printf("ch:%c\n",ch);
1660             if (ch!='=')
1661                 break;
1662             p++;
1663             p = skipwhite(p);
1664             // ch = parsebuf[p];
1665             // printf("ch:%c\n",ch);
1666             String attrVal;
1667             p2 = getQuoted(p, attrVal, true);
1668             p=p2+1;
1669             //printf("name:'%s'   value:'%s'\n",attrName.c_str(),attrVal.c_str());
1670             char *namestr = (char *)attrName.c_str();
1671             if (strncmp(namestr, "xmlns:", 6)==0)
1672                 n->addNamespace(attrName, attrVal);
1673             else
1674                 n->addAttribute(attrName, attrVal);
1675             }
1676         }
1678     bool cdata = false;
1680     p++;
1681     // ### Get intervening data ### */
1682     String data;
1683     while (p<parselen)
1684         {
1685         //# COMMENT
1686         p2 = match(p, "<!--");
1687         if (!cdata && p2>p)
1688             {
1689             p = p2;
1690             while (p<parselen)
1691                 {
1692                 p2 = match(p, "-->");
1693                 if (p2 > p)
1694                     {
1695                     p = p2;
1696                     break;
1697                     }
1698                 p++;
1699                 }
1700             }
1702         ch = peek(p);
1703         //# END TAG
1704         if (ch=='<' && !cdata && peek(p+1)=='/')
1705             {
1706             break;
1707             }
1708         //# CDATA
1709         p2 = match(p, "<![CDATA[");
1710         if (p2 > p)
1711             {
1712             cdata = true;
1713             p = p2;
1714             continue;
1715             }
1717         //# CHILD ELEMENT
1718         if (ch == '<')
1719             {
1720             p2 = parseElement(p, n, depth+1);
1721             if (p2 == p)
1722                 {
1723                 /*
1724                 printf("problem on element:%s.  p2:%d p:%d\n",
1725                       openTagName.c_str(), p2, p);
1726                 */
1727                 return p0;
1728                 }
1729             p = p2;
1730             continue;
1731             }
1732         //# ENTITY
1733         if (ch=='&' && !cdata)
1734             {
1735             bool found = false;
1736             for (EntityEntry *ee = entities ; ee->value ; ee++)
1737                 {
1738                 int p2 = match(p, ee->escaped);
1739                 if (p2>p)
1740                     {
1741                     data.push_back(ee->value);
1742                     p = p2;
1743                     found = true;
1744                     break;
1745                     }
1746                 }
1747             if (!found)
1748                 {
1749                 error("unterminated entity");
1750                 return -1;
1751                 }
1752             continue;
1753             }
1755         //# NONE OF THE ABOVE
1756         data.push_back(ch);
1757         p++;
1758         }/*while*/
1761     n->value = data;
1762     //printf("%d : data:%s\n",p,data.c_str());
1764     //## Get close tag
1765     p = skipwhite(p);
1766     ch = peek(p);
1767     if (ch != '<')
1768         {
1769         error("no < for end tag\n");
1770         return p0;
1771         }
1772     p++;
1773     ch = peek(p);
1774     if (ch != '/')
1775         {
1776         error("no / on end tag");
1777         return p0;
1778         }
1779     p++;
1780     ch = peek(p);
1781     p = skipwhite(p);
1782     String closeTagName;
1783     p = getWord(p, closeTagName);
1784     if (openTagName != closeTagName)
1785         {
1786         error("Mismatched closing tag.  Expected </%S>. Got '%S'.",
1787                 openTagName.c_str(), closeTagName.c_str());
1788         return p0;
1789         }
1790     p = skipwhite(p);
1791     if (peek(p) != '>')
1792         {
1793         error("no > on end tag for '%s'", closeTagName.c_str());
1794         return p0;
1795         }
1796     p++;
1797     // printf("close element:%s\n",closeTagName.c_str());
1798     p = skipwhite(p);
1799     return p;
1805 Element *Parser::parse(XMLCh *buf,int pos,int len)
1807     parselen = len;
1808     parsebuf = buf;
1809     Element *rootNode = new Element("root");
1810     pos = parseVersion(pos);
1811     pos = parseDoctype(pos);
1812     pos = parseElement(pos, rootNode, 0);
1813     return rootNode;
1817 Element *Parser::parse(const char *buf, int pos, int len)
1819     XMLCh *charbuf = new XMLCh[len + 1];
1820     long i = 0;
1821     for ( ; i < len ; i++)
1822         charbuf[i] = (XMLCh)buf[i];
1823     charbuf[i] = '\0';
1825     Element *n = parse(charbuf, pos, len);
1826     delete[] charbuf;
1827     return n;
1830 Element *Parser::parse(const String &buf)
1832     long len = (long)buf.size();
1833     XMLCh *charbuf = new XMLCh[len + 1];
1834     long i = 0;
1835     for ( ; i < len ; i++)
1836         charbuf[i] = (XMLCh)buf[i];
1837     charbuf[i] = '\0';
1839     Element *n = parse(charbuf, 0, len);
1840     delete[] charbuf;
1841     return n;
1844 Element *Parser::parseFile(const String &fileName)
1847     //##### LOAD INTO A CHAR BUF, THEN CONVERT TO XMLCh
1848     FILE *f = fopen(fileName.c_str(), "rb");
1849     if (!f)
1850         return NULL;
1852     struct stat  statBuf;
1853     if (fstat(fileno(f),&statBuf)<0)
1854         {
1855         fclose(f);
1856         return NULL;
1857         }
1858     long filelen = statBuf.st_size;
1860     //printf("length:%d\n",filelen);
1861     XMLCh *charbuf = new XMLCh[filelen + 1];
1862     for (XMLCh *p=charbuf ; !feof(f) ; p++)
1863         {
1864         *p = (XMLCh)fgetc(f);
1865         }
1866     fclose(f);
1867     charbuf[filelen] = '\0';
1870     /*
1871     printf("nrbytes:%d\n",wc_count);
1872     printf("buf:%ls\n======\n",charbuf);
1873     */
1874     Element *n = parse(charbuf, 0, filelen);
1875     delete[] charbuf;
1876     return n;
1879 //########################################################################
1880 //########################################################################
1881 //##  E N D    X M L
1882 //########################################################################
1883 //########################################################################
1890 //########################################################################
1891 //########################################################################
1892 //##  U R I
1893 //########################################################################
1894 //########################################################################
1896 //This would normally be a call to a UNICODE function
1897 #define isLetter(x) isalpha(x)
1899 /**
1900  *  A class that implements the W3C URI resource reference.
1901  */
1902 class URI
1904 public:
1906     typedef enum
1907         {
1908         SCHEME_NONE =0,
1909         SCHEME_DATA,
1910         SCHEME_HTTP,
1911         SCHEME_HTTPS,
1912         SCHEME_FTP,
1913         SCHEME_FILE,
1914         SCHEME_LDAP,
1915         SCHEME_MAILTO,
1916         SCHEME_NEWS,
1917         SCHEME_TELNET
1918         } SchemeTypes;
1920     /**
1921      *
1922      */
1923     URI()
1924         {
1925         init();
1926         }
1928     /**
1929      *
1930      */
1931     URI(const String &str)
1932         {
1933         init();
1934         parse(str);
1935         }
1938     /**
1939      *
1940      */
1941     URI(const char *str)
1942         {
1943         init();
1944         String domStr = str;
1945         parse(domStr);
1946         }
1949     /**
1950      *
1951      */
1952     URI(const URI &other)
1953         {
1954         init();
1955         assign(other);
1956         }
1959     /**
1960      *
1961      */
1962     URI &operator=(const URI &other)
1963         {
1964         init();
1965         assign(other);
1966         return *this;
1967         }
1970     /**
1971      *
1972      */
1973     virtual ~URI()
1974         {}
1978     /**
1979      *
1980      */
1981     virtual bool parse(const String &str);
1983     /**
1984      *
1985      */
1986     virtual String toString() const;
1988     /**
1989      *
1990      */
1991     virtual int getScheme() const;
1993     /**
1994      *
1995      */
1996     virtual String getSchemeStr() const;
1998     /**
1999      *
2000      */
2001     virtual String getAuthority() const;
2003     /**
2004      *  Same as getAuthority, but if the port has been specified
2005      *  as host:port , the port will not be included
2006      */
2007     virtual String getHost() const;
2009     /**
2010      *
2011      */
2012     virtual int getPort() const;
2014     /**
2015      *
2016      */
2017     virtual String getPath() const;
2019     /**
2020      *
2021      */
2022     virtual String getNativePath() const;
2024     /**
2025      *
2026      */
2027     virtual bool isAbsolute() const;
2029     /**
2030      *
2031      */
2032     virtual bool isOpaque() const;
2034     /**
2035      *
2036      */
2037     virtual String getQuery() const;
2039     /**
2040      *
2041      */
2042     virtual String getFragment() const;
2044     /**
2045      *
2046      */
2047     virtual URI resolve(const URI &other) const;
2049     /**
2050      *
2051      */
2052     virtual void normalize();
2054 private:
2056     /**
2057      *
2058      */
2059     void init()
2060         {
2061         parsebuf  = NULL;
2062         parselen  = 0;
2063         scheme    = SCHEME_NONE;
2064         schemeStr = "";
2065         port      = 0;
2066         authority = "";
2067         path      = "";
2068         absolute  = false;
2069         opaque    = false;
2070         query     = "";
2071         fragment  = "";
2072         }
2075     /**
2076      *
2077      */
2078     void assign(const URI &other)
2079         {
2080         scheme    = other.scheme;
2081         schemeStr = other.schemeStr;
2082         authority = other.authority;
2083         port      = other.port;
2084         path      = other.path;
2085         absolute  = other.absolute;
2086         opaque    = other.opaque;
2087         query     = other.query;
2088         fragment  = other.fragment;
2089         }
2091     int scheme;
2093     String schemeStr;
2095     String authority;
2097     bool portSpecified;
2099     int port;
2101     String path;
2103     bool absolute;
2105     bool opaque;
2107     String query;
2109     String fragment;
2111     void error(const char *fmt, ...);
2113     void trace(const char *fmt, ...);
2116     int peek(int p);
2118     int match(int p, char *key);
2120     int parseScheme(int p);
2122     int parseHierarchicalPart(int p0);
2124     int parseQuery(int p0);
2126     int parseFragment(int p0);
2128     int parse(int p);
2130     char *parsebuf;
2132     int parselen;
2134 };
2138 typedef struct
2140     int  ival;
2141     char *sval;
2142     int  port;
2143 } LookupEntry;
2145 LookupEntry schemes[] =
2147     { URI::SCHEME_DATA,   "data:",    0 },
2148     { URI::SCHEME_HTTP,   "http:",   80 },
2149     { URI::SCHEME_HTTPS,  "https:", 443 },
2150     { URI::SCHEME_FTP,    "ftp",     12 },
2151     { URI::SCHEME_FILE,   "file:",    0 },
2152     { URI::SCHEME_LDAP,   "ldap:",  123 },
2153     { URI::SCHEME_MAILTO, "mailto:", 25 },
2154     { URI::SCHEME_NEWS,   "news:",  117 },
2155     { URI::SCHEME_TELNET, "telnet:", 23 },
2156     { 0,                  NULL,       0 }
2157 };
2160 String URI::toString() const
2162     String str = schemeStr;
2163     if (authority.size() > 0)
2164         {
2165         str.append("//");
2166         str.append(authority);
2167         }
2168     str.append(path);
2169     if (query.size() > 0)
2170         {
2171         str.append("?");
2172         str.append(query);
2173         }
2174     if (fragment.size() > 0)
2175         {
2176         str.append("#");
2177         str.append(fragment);
2178         }
2179     return str;
2183 int URI::getScheme() const
2185     return scheme;
2188 String URI::getSchemeStr() const
2190     return schemeStr;
2194 String URI::getAuthority() const
2196     String ret = authority;
2197     if (portSpecified && port>=0)
2198         {
2199         char buf[7];
2200         snprintf(buf, 6, ":%6d", port);
2201         ret.append(buf);
2202         }
2203     return ret;
2206 String URI::getHost() const
2208     return authority;
2211 int URI::getPort() const
2213     return port;
2217 String URI::getPath() const
2219     return path;
2222 String URI::getNativePath() const
2224     String npath;
2225 #ifdef __WIN32__
2226     unsigned int firstChar = 0;
2227     if (path.size() >= 3)
2228         {
2229         if (path[0] == '/' &&
2230             isLetter(path[1]) &&
2231             path[2] == ':')
2232             firstChar++;
2233          }
2234     for (unsigned int i=firstChar ; i<path.size() ; i++)
2235         {
2236         XMLCh ch = (XMLCh) path[i];
2237         if (ch == '/')
2238             npath.push_back((XMLCh)'\\');
2239         else
2240             npath.push_back(ch);
2241         }
2242 #else
2243     npath = path;
2244 #endif
2245     return npath;
2249 bool URI::isAbsolute() const
2251     return absolute;
2254 bool URI::isOpaque() const
2256     return opaque;
2260 String URI::getQuery() const
2262     return query;
2266 String URI::getFragment() const
2268     return fragment;
2272 URI URI::resolve(const URI &other) const
2274     //### According to w3c, this is handled in 3 cases
2276     //## 1
2277     if (opaque || other.isAbsolute())
2278         return other;
2280     //## 2
2281     if (other.fragment.size()  >  0 &&
2282         other.path.size()      == 0 &&
2283         other.scheme           == SCHEME_NONE &&
2284         other.authority.size() == 0 &&
2285         other.query.size()     == 0 )
2286         {
2287         URI fragUri = *this;
2288         fragUri.fragment = other.fragment;
2289         return fragUri;
2290         }
2292     //## 3 http://www.ietf.org/rfc/rfc2396.txt, section 5.2
2293     URI newUri;
2294     //# 3.1
2295     newUri.scheme    = scheme;
2296     newUri.schemeStr = schemeStr;
2297     newUri.query     = other.query;
2298     newUri.fragment  = other.fragment;
2299     if (other.authority.size() > 0)
2300         {
2301         //# 3.2
2302         if (absolute || other.absolute)
2303             newUri.absolute = true;
2304         newUri.authority = other.authority;
2305         newUri.port      = other.port;//part of authority
2306         newUri.path      = other.path;
2307         }
2308     else
2309         {
2310         //# 3.3
2311         if (other.absolute)
2312             {
2313             newUri.absolute = true;
2314             newUri.path     = other.path;
2315             }
2316         else
2317             {
2318             unsigned int pos = path.find_last_of('/');
2319             if (pos != path.npos)
2320                 {
2321                 String tpath = path.substr(0, pos+1);
2322                 tpath.append(other.path);
2323                 newUri.path = tpath;
2324                 }
2325             else
2326                 newUri.path = other.path;
2327             }
2328         }
2330     newUri.normalize();
2331     return newUri;
2336 /**
2337  *  This follows the Java URI algorithm:
2338  *   1. All "." segments are removed.
2339  *   2. If a ".." segment is preceded by a non-".." segment
2340  *          then both of these segments are removed. This step
2341  *          is repeated until it is no longer applicable.
2342  *   3. If the path is relative, and if its first segment
2343  *          contains a colon character (':'), then a "." segment
2344  *          is prepended. This prevents a relative URI with a path
2345  *          such as "a:b/c/d" from later being re-parsed as an
2346  *          opaque URI with a scheme of "a" and a scheme-specific
2347  *          part of "b/c/d". (Deviation from RFC 2396)
2348  */
2349 void URI::normalize()
2351     std::vector<String> segments;
2353     //## Collect segments
2354     if (path.size()<2)
2355         return;
2356     bool abs = false;
2357     unsigned int pos=0;
2358     if (path[0]=='/')
2359         {
2360         abs = true;
2361         pos++;
2362         }
2363     while (pos < path.size())
2364         {
2365         unsigned int pos2 = path.find('/', pos);
2366         if (pos2==path.npos)
2367             {
2368             String seg = path.substr(pos);
2369             //printf("last segment:%s\n", seg.c_str());
2370             segments.push_back(seg);
2371             break;
2372             }
2373         if (pos2>pos)
2374             {
2375             String seg = path.substr(pos, pos2-pos);
2376             //printf("segment:%s\n", seg.c_str());
2377             segments.push_back(seg);
2378             }
2379         pos = pos2;
2380         pos++;
2381         }
2383     //## Clean up (normalize) segments
2384     bool edited = false;
2385     std::vector<String>::iterator iter;
2386     for (iter=segments.begin() ; iter!=segments.end() ; )
2387         {
2388         String s = *iter;
2389         if (s == ".")
2390             {
2391             iter = segments.erase(iter);
2392             edited = true;
2393             }
2394         else if (s == ".." &&
2395                  iter != segments.begin() &&
2396                  *(iter-1) != "..")
2397             {
2398             iter--; //back up, then erase two entries
2399             iter = segments.erase(iter);
2400             iter = segments.erase(iter);
2401             edited = true;
2402             }
2403         else
2404             iter++;
2405         }
2407     //## Rebuild path, if necessary
2408     if (edited)
2409         {
2410         path.clear();
2411         if (abs)
2412             {
2413             path.append("/");
2414             }
2415         std::vector<String>::iterator iter;
2416         for (iter=segments.begin() ; iter!=segments.end() ; iter++)
2417             {
2418             if (iter != segments.begin())
2419                 path.append("/");
2420             path.append(*iter);
2421             }
2422         }
2428 //#########################################################################
2429 //# M E S S A G E S
2430 //#########################################################################
2432 void URI::error(const char *fmt, ...)
2434     va_list args;
2435     fprintf(stderr, "URI error: ");
2436     va_start(args, fmt);
2437     vfprintf(stderr, fmt, args);
2438     va_end(args);
2439     fprintf(stderr, "\n");
2442 void URI::trace(const char *fmt, ...)
2444     va_list args;
2445     fprintf(stdout, "URI: ");
2446     va_start(args, fmt);
2447     vfprintf(stdout, fmt, args);
2448     va_end(args);
2449     fprintf(stdout, "\n");
2455 //#########################################################################
2456 //# P A R S I N G
2457 //#########################################################################
2461 int URI::peek(int p)
2463     if (p<0 || p>=parselen)
2464         return -1;
2465     return parsebuf[p];
2470 int URI::match(int p0, char *key)
2472     int p = p0;
2473     while (p < parselen)
2474         {
2475         if (*key == '\0')
2476             return p;
2477         else if (*key != parsebuf[p])
2478             break;
2479         p++; key++;
2480         }
2481     return p0;
2484 //#########################################################################
2485 //#  Parsing is performed according to:
2486 //#  http://www.gbiv.com/protocols/uri/rfc/rfc3986.html#components
2487 //#########################################################################
2489 int URI::parseScheme(int p0)
2491     int p = p0;
2492     for (LookupEntry *entry = schemes; entry->sval ; entry++)
2493         {
2494         int p2 = match(p, entry->sval);
2495         if (p2 > p)
2496             {
2497             schemeStr = entry->sval;
2498             scheme    = entry->ival;
2499             port      = entry->port;
2500             p = p2;
2501             return p;
2502             }
2503         }
2505     return p;
2509 int URI::parseHierarchicalPart(int p0)
2511     int p = p0;
2512     int ch;
2514     //# Authority field (host and port, for example)
2515     int p2 = match(p, "//");
2516     if (p2 > p)
2517         {
2518         p = p2;
2519         portSpecified = false;
2520         String portStr;
2521         while (p < parselen)
2522             {
2523             ch = peek(p);
2524             if (ch == '/')
2525                 break;
2526             else if (ch == ':')
2527                 portSpecified = true;
2528             else if (portSpecified)
2529                 portStr.push_back((XMLCh)ch);
2530             else
2531                 authority.push_back((XMLCh)ch);
2532             p++;
2533             }
2534         if (portStr.size() > 0)
2535             {
2536             char *pstr = (char *)portStr.c_str();
2537             char *endStr;
2538             long val = strtol(pstr, &endStr, 10);
2539             if (endStr > pstr) //successful parse?
2540                 port = val;
2541             }
2542         }
2544     //# Are we absolute?
2545     ch = peek(p);
2546     if (isLetter(ch) && peek(p+1)==':')
2547         {
2548         absolute = true;
2549         path.push_back((XMLCh)'/');
2550         }
2551     else if (ch == '/')
2552         {
2553         absolute = true;
2554         if (p>p0) //in other words, if '/' is not the first char
2555             opaque = true;
2556         path.push_back((XMLCh)ch);
2557         p++;
2558         }
2560     while (p < parselen)
2561         {
2562         ch = peek(p);
2563         if (ch == '?' || ch == '#')
2564             break;
2565         path.push_back((XMLCh)ch);
2566         p++;
2567         }
2569     return p;
2572 int URI::parseQuery(int p0)
2574     int p = p0;
2575     int ch = peek(p);
2576     if (ch != '?')
2577         return p0;
2579     p++;
2580     while (p < parselen)
2581         {
2582         ch = peek(p);
2583         if (ch == '#')
2584             break;
2585         query.push_back((XMLCh)ch);
2586         p++;
2587         }
2590     return p;
2593 int URI::parseFragment(int p0)
2596     int p = p0;
2597     int ch = peek(p);
2598     if (ch != '#')
2599         return p0;
2601     p++;
2602     while (p < parselen)
2603         {
2604         ch = peek(p);
2605         if (ch == '?')
2606             break;
2607         fragment.push_back((XMLCh)ch);
2608         p++;
2609         }
2612     return p;
2616 int URI::parse(int p0)
2619     int p = p0;
2621     int p2 = parseScheme(p);
2622     if (p2 < 0)
2623         {
2624         error("Scheme");
2625         return -1;
2626         }
2627     p = p2;
2630     p2 = parseHierarchicalPart(p);
2631     if (p2 < 0)
2632         {
2633         error("Hierarchical part");
2634         return -1;
2635         }
2636     p = p2;
2638     p2 = parseQuery(p);
2639     if (p2 < 0)
2640         {
2641         error("Query");
2642         return -1;
2643         }
2644     p = p2;
2647     p2 = parseFragment(p);
2648     if (p2 < 0)
2649         {
2650         error("Fragment");
2651         return -1;
2652         }
2653     p = p2;
2655     return p;
2661 bool URI::parse(const String &str)
2663     init();
2664     
2665     parselen = str.size();
2667     String tmp;
2668     for (unsigned int i=0 ; i<str.size() ; i++)
2669         {
2670         XMLCh ch = (XMLCh) str[i];
2671         if (ch == '\\')
2672             tmp.push_back((XMLCh)'/');
2673         else
2674             tmp.push_back(ch);
2675         }
2676     parsebuf = (char *) tmp.c_str();
2679     int p = parse(0);
2680     normalize();
2682     if (p < 0)
2683         {
2684         error("Syntax error");
2685         return false;
2686         }
2688     //printf("uri:%s\n", toString().c_str());
2689     //printf("path:%s\n", path.c_str());
2691     return true;
2702 //########################################################################
2703 //########################################################################
2704 //##  M A K E
2705 //########################################################################
2706 //########################################################################
2708 //########################################################################
2709 //# F I L E S E T
2710 //########################################################################
2711 /**
2712  * This is the descriptor for a <fileset> item
2713  */
2714 class FileSet
2716 public:
2718     /**
2719      *
2720      */
2721     FileSet()
2722         {}
2724     /**
2725      *
2726      */
2727     FileSet(const FileSet &other)
2728         { assign(other); }
2730     /**
2731      *
2732      */
2733     FileSet &operator=(const FileSet &other)
2734         { assign(other); return *this; }
2736     /**
2737      *
2738      */
2739     virtual ~FileSet()
2740         {}
2742     /**
2743      *
2744      */
2745     String getDirectory()
2746         { return directory; }
2747         
2748     /**
2749      *
2750      */
2751     void setDirectory(const String &val)
2752         { directory = val; }
2754     /**
2755      *
2756      */
2757     void setFiles(const std::vector<String> &val)
2758         { files = val; }
2760     /**
2761      *
2762      */
2763     std::vector<String> getFiles()
2764         { return files; }
2765         
2766     /**
2767      *
2768      */
2769     void setIncludes(const std::vector<String> &val)
2770         { includes = val; }
2772     /**
2773      *
2774      */
2775     std::vector<String> getIncludes()
2776         { return includes; }
2777         
2778     /**
2779      *
2780      */
2781     void setExcludes(const std::vector<String> &val)
2782         { excludes = val; }
2784     /**
2785      *
2786      */
2787     std::vector<String> getExcludes()
2788         { return excludes; }
2789         
2790     /**
2791      *
2792      */
2793     unsigned int size()
2794         { return files.size(); }
2795         
2796     /**
2797      *
2798      */
2799     String operator[](int index)
2800         { return files[index]; }
2801         
2802     /**
2803      *
2804      */
2805     void clear()
2806         {
2807         directory = "";
2808         files.clear();
2809         includes.clear();
2810         excludes.clear();
2811         }
2812         
2814 private:
2816     void assign(const FileSet &other)
2817         {
2818         directory = other.directory;
2819         files     = other.files;
2820         includes  = other.includes;
2821         excludes  = other.excludes;
2822         }
2824     String directory;
2825     std::vector<String> files;
2826     std::vector<String> includes;
2827     std::vector<String> excludes;
2828 };
2833 //########################################################################
2834 //# M A K E    B A S E
2835 //########################################################################
2836 /**
2837  * Base class for all classes in this file
2838  */
2839 class MakeBase
2841 public:
2842     MakeBase()
2843         {}
2844     virtual ~MakeBase()
2845         {}
2847     /**
2848      *     Return the URI of the file associated with this object 
2849      */     
2850     URI getURI()
2851         { return uri; }
2853     /**
2854      * Set the uri to the given string
2855      */
2856     void setURI(const String &uristr)
2857         { uri.parse(uristr); }
2859     /**
2860      *  Resolve another path relative to this one
2861      */
2862     String resolve(const String &otherPath);
2864     /**
2865      *  Get an element attribute, performing substitutions if necessary
2866      */
2867     bool getAttribute(Element *elem, const String &name, String &result);
2869     /**
2870      * Get an element value, performing substitutions if necessary
2871      */
2872     bool getValue(Element *elem, String &result);
2874 protected:
2876     /**
2877      *    The path to the file associated with this object
2878      */     
2879     URI uri;
2882     /**
2883      *  Print a printf()-like formatted error message
2884      */
2885     void error(char *fmt, ...);
2887     /**
2888      *  Print a printf()-like formatted trace message
2889      */
2890     void status(char *fmt, ...);
2892     /**
2893      *  Print a printf()-like formatted trace message
2894      */
2895     void trace(char *fmt, ...);
2897     /**
2898      *  Check if a given string matches a given regex pattern
2899      */
2900     bool regexMatch(const String &str, const String &pattern);
2902     /**
2903      *
2904      */
2905     String getSuffix(const String &fname);
2907     /**
2908      * Break up a string into substrings delimited the characters
2909      * in delimiters.  Null-length substrings are ignored
2910      */  
2911     std::vector<String> tokenize(const String &val,
2912                           const String &delimiters);
2914     /**
2915      *  replace runs of whitespace with a space
2916      */
2917     String strip(const String &s);
2919     /**
2920      *  remove leading whitespace from each line
2921      */
2922     String leftJustify(const String &s);
2924     /**
2925      *  remove leading and trailing whitespace from string
2926      */
2927     String trim(const String &s);
2929     /**
2930      * Return the native format of the canonical
2931      * path which we store
2932      */
2933     String getNativePath(const String &path);
2935     /**
2936      * Execute a shell command.  Outbuf is a ref to a string
2937      * to catch the result.     
2938      */         
2939     bool executeCommand(const String &call,
2940                         const String &inbuf,
2941                         String &outbuf,
2942                         String &errbuf);
2943     /**
2944      * List all directories in a given base and starting directory
2945      * It is usually called like:
2946      *        bool ret = listDirectories("src", "", result);    
2947      */         
2948     bool listDirectories(const String &baseName,
2949                          const String &dirname,
2950                          std::vector<String> &res);
2952     /**
2953      * Find all files in the named directory 
2954      */         
2955     bool listFiles(const String &baseName,
2956                    const String &dirname,
2957                    std::vector<String> &result);
2959     /**
2960      * Perform a listing for a fileset 
2961      */         
2962     bool listFiles(MakeBase &propRef, FileSet &fileSet);
2964     /**
2965      * Parse a <patternset>
2966      */  
2967     bool parsePatternSet(Element *elem,
2968                        MakeBase &propRef,
2969                        std::vector<String> &includes,
2970                        std::vector<String> &excludes);
2972     /**
2973      * Parse a <fileset> entry, and determine which files
2974      * should be included
2975      */  
2976     bool parseFileSet(Element *elem,
2977                     MakeBase &propRef,
2978                     FileSet &fileSet);
2980     /**
2981      * Return this object's property list
2982      */
2983     virtual std::map<String, String> &getProperties()
2984         { return properties; }
2986     /**
2987      * Return a named property if found, else a null string
2988      */
2989     virtual String getProperty(const String &name)
2990         {
2991         String val;
2992         std::map<String, String>::iterator iter;
2993         iter = properties.find(name);
2994         if (iter != properties.end())
2995             val = iter->second;
2996         return val;
2997         }
3000     std::map<String, String> properties;
3002     /**
3003      * Turn 'true' and 'false' into boolean values
3004      */             
3005     bool getBool(const String &str, bool &val);
3007     /**
3008      * Create a directory, making intermediate dirs
3009      * if necessary
3010      */                  
3011     bool createDirectory(const String &dirname);
3013     /**
3014      * Delete a directory and its children if desired
3015      */
3016     bool removeDirectory(const String &dirName);
3018     /**
3019      * Copy a file from one name to another. Perform only if needed
3020      */ 
3021     bool copyFile(const String &srcFile, const String &destFile);
3023     /**
3024      * Tests if the file exists and is a regular file
3025      */ 
3026     bool isRegularFile(const String &fileName);
3028     /**
3029      * Tests if the file exists and is a directory
3030      */ 
3031     bool isDirectory(const String &fileName);
3033     /**
3034      * Tests is the modification date of fileA is newer than fileB
3035      */ 
3036     bool isNewerThan(const String &fileA, const String &fileB);
3038 private:
3040     /**
3041      * replace variable refs like ${a} with their values
3042      */         
3043     bool getSubstitutions(const String &s, String &result);
3047 };
3052 /**
3053  *  Print a printf()-like formatted error message
3054  */
3055 void MakeBase::error(char *fmt, ...)
3057     va_list args;
3058     va_start(args,fmt);
3059     fprintf(stderr, "Make error: ");
3060     vfprintf(stderr, fmt, args);
3061     fprintf(stderr, "\n");
3062     va_end(args) ;
3067 /**
3068  *  Print a printf()-like formatted trace message
3069  */
3070 void MakeBase::status(char *fmt, ...)
3072     va_list args;
3073     va_start(args,fmt);
3074     //fprintf(stdout, " ");
3075     vfprintf(stdout, fmt, args);
3076     fprintf(stdout, "\n");
3077     va_end(args) ;
3082 /**
3083  *  Resolve another path relative to this one
3084  */
3085 String MakeBase::resolve(const String &otherPath)
3087     URI otherURI(otherPath);
3088     URI fullURI = uri.resolve(otherURI);
3089     String ret = fullURI.toString();
3090     return ret;
3094 /**
3095  *  Print a printf()-like formatted trace message
3096  */
3097 void MakeBase::trace(char *fmt, ...)
3099     va_list args;
3100     va_start(args,fmt);
3101     fprintf(stdout, "Make: ");
3102     vfprintf(stdout, fmt, args);
3103     fprintf(stdout, "\n");
3104     va_end(args) ;
3109 /**
3110  *  Check if a given string matches a given regex pattern
3111  */
3112 bool MakeBase::regexMatch(const String &str, const String &pattern)
3114     const TRexChar *terror = NULL;
3115     const TRexChar *cpat = pattern.c_str();
3116     TRex *expr = trex_compile(cpat, &terror);
3117     if (!expr)
3118         {
3119         if (!terror)
3120             terror = "undefined";
3121         error("compilation error [%s]!\n", terror);
3122         return false;
3123         } 
3125     bool ret = true;
3127     const TRexChar *cstr = str.c_str();
3128     if (trex_match(expr, cstr))
3129         {
3130         ret = true;
3131         }
3132     else
3133         {
3134         ret = false;
3135         }
3137     trex_free(expr);
3139     return ret;
3142 /**
3143  *  Return the suffix, if any, of a file name
3144  */
3145 String MakeBase::getSuffix(const String &fname)
3147     if (fname.size() < 2)
3148         return "";
3149     unsigned int pos = fname.find_last_of('.');
3150     if (pos == fname.npos)
3151         return "";
3152     pos++;
3153     String res = fname.substr(pos, fname.size()-pos);
3154     //trace("suffix:%s", res.c_str()); 
3155     return res;
3160 /**
3161  * Break up a string into substrings delimited the characters
3162  * in delimiters.  Null-length substrings are ignored
3163  */  
3164 std::vector<String> MakeBase::tokenize(const String &str,
3165                                 const String &delimiters)
3168     std::vector<String> res;
3169     char *del = (char *)delimiters.c_str();
3170     String dmp;
3171     for (unsigned int i=0 ; i<str.size() ; i++)
3172         {
3173         char ch = str[i];
3174         char *p = (char *)0;
3175         for (p=del ; *p ; p++)
3176             if (*p == ch)
3177                 break;
3178         if (*p)
3179             {
3180             if (dmp.size() > 0)
3181                 {
3182                 res.push_back(dmp);
3183                 dmp.clear();
3184                 }
3185             }
3186         else
3187             {
3188             dmp.push_back(ch);
3189             }
3190         }
3191     //Add tail
3192     if (dmp.size() > 0)
3193         {
3194         res.push_back(dmp);
3195         dmp.clear();
3196         }
3198     return res;
3203 /**
3204  *  replace runs of whitespace with a single space
3205  */
3206 String MakeBase::strip(const String &s)
3208     int len = s.size();
3209     String stripped;
3210     for (int i = 0 ; i<len ; i++)
3211         {
3212         char ch = s[i];
3213         if (isspace(ch))
3214             {
3215             stripped.push_back(' ');
3216             for ( ; i<len ; i++)
3217                 {
3218                 ch = s[i];
3219                 if (!isspace(ch))
3220                     {
3221                     stripped.push_back(ch);
3222                     break;
3223                     }
3224                 }
3225             }
3226         else
3227             {
3228             stripped.push_back(ch);
3229             }
3230         }
3231     return stripped;
3234 /**
3235  *  remove leading whitespace from each line
3236  */
3237 String MakeBase::leftJustify(const String &s)
3239     String out;
3240     int len = s.size();
3241     for (int i = 0 ; i<len ; )
3242         {
3243         char ch;
3244         //Skip to first visible character
3245         while (i<len)
3246             {
3247             ch = s[i];
3248             if (ch == '\n' || ch == '\r'
3249               || !isspace(ch))
3250                   break;
3251             i++;
3252             }
3253         //Copy the rest of the line
3254         while (i<len)
3255             {
3256             ch = s[i];
3257             if (ch == '\n' || ch == '\r')
3258                 {
3259                 if (ch != '\r')
3260                     out.push_back('\n');
3261                 i++;
3262                 break;
3263                 }
3264             else
3265                 {
3266                 out.push_back(ch);
3267                 }
3268             i++;
3269             }
3270         }
3271     return out;
3275 /**
3276  *  Removes whitespace from beginning and end of a string
3277  */
3278 String MakeBase::trim(const String &s)
3280     if (s.size() < 1)
3281         return s;
3282     
3283     //Find first non-ws char
3284     unsigned int begin = 0;
3285     for ( ; begin < s.size() ; begin++)
3286         {
3287         if (!isspace(s[begin]))
3288             break;
3289         }
3291     //Find first non-ws char, going in reverse
3292     unsigned int end = s.size() - 1;
3293     for ( ; end > begin ; end--)
3294         {
3295         if (!isspace(s[end]))
3296             break;
3297         }
3298     //trace("begin:%d  end:%d", begin, end);
3300     String res = s.substr(begin, end-begin+1);
3301     return res;
3304 /**
3305  * Return the native format of the canonical
3306  * path which we store
3307  */
3308 String MakeBase::getNativePath(const String &path)
3310 #ifdef __WIN32__
3311     String npath;
3312     unsigned int firstChar = 0;
3313     if (path.size() >= 3)
3314         {
3315         if (path[0] == '/' &&
3316             isalpha(path[1]) &&
3317             path[2] == ':')
3318             firstChar++;
3319         }
3320     for (unsigned int i=firstChar ; i<path.size() ; i++)
3321         {
3322         char ch = path[i];
3323         if (ch == '/')
3324             npath.push_back('\\');
3325         else
3326             npath.push_back(ch);
3327         }
3328     return npath;
3329 #else
3330     return path;
3331 #endif
3335 #ifdef __WIN32__
3336 #include <tchar.h>
3338 static String win32LastError()
3341     DWORD dw = GetLastError(); 
3343     LPVOID str;
3344     FormatMessage(
3345         FORMAT_MESSAGE_ALLOCATE_BUFFER | 
3346         FORMAT_MESSAGE_FROM_SYSTEM,
3347         NULL,
3348         dw,
3349         0,
3350         (LPTSTR) &str,
3351         0, NULL );
3352     LPTSTR p = _tcschr((const char *)str, _T('\r'));
3353     if(p != NULL)
3354         { // lose CRLF
3355         *p = _T('\0');
3356         }
3357     String ret = (char *)str;
3358     LocalFree(str);
3360     return ret;
3362 #endif
3366 /**
3367  * Execute a system call, using pipes to send data to the
3368  * program's stdin,  and reading stdout and stderr.
3369  */
3370 bool MakeBase::executeCommand(const String &command,
3371                               const String &inbuf,
3372                               String &outbuf,
3373                               String &errbuf)
3376     status("============ cmd ============\n%s\n=============================",
3377                 command.c_str());
3379     outbuf.clear();
3380     errbuf.clear();
3381     
3382 #ifdef __WIN32__
3384     /*
3385     I really hate having win32 code in this program, but the
3386     read buffer in command.com and cmd.exe are just too small
3387     for the large commands we need for compiling and linking.
3388     */
3390     bool ret = true;
3392     //# Allocate a separate buffer for safety
3393     char *paramBuf = new char[command.size() + 1];
3394     if (!paramBuf)
3395        {
3396        error("executeCommand cannot allocate command buffer");
3397        return false;
3398        }
3399     strcpy(paramBuf, (char *)command.c_str());
3401     //# Go to http://msdn2.microsoft.com/en-us/library/ms682499.aspx
3402     //# to see how Win32 pipes work
3404     //# Create pipes
3405     SECURITY_ATTRIBUTES saAttr; 
3406     saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); 
3407     saAttr.bInheritHandle = TRUE; 
3408     saAttr.lpSecurityDescriptor = NULL; 
3409     HANDLE stdinRead,  stdinWrite;
3410     HANDLE stdoutRead, stdoutWrite;
3411     HANDLE stderrRead, stderrWrite;
3412     if (!CreatePipe(&stdinRead, &stdinWrite, &saAttr, 0))
3413         {
3414         error("executeProgram: could not create pipe");
3415         delete[] paramBuf;
3416         return false;
3417         } 
3418     SetHandleInformation(stdinWrite, HANDLE_FLAG_INHERIT, 0);
3419     if (!CreatePipe(&stdoutRead, &stdoutWrite, &saAttr, 0))
3420         {
3421         error("executeProgram: could not create pipe");
3422         delete[] paramBuf;
3423         return false;
3424         } 
3425     SetHandleInformation(stdoutRead, HANDLE_FLAG_INHERIT, 0);
3426     if (!CreatePipe(&stderrRead, &stderrWrite, &saAttr, 0))
3427         {
3428         error("executeProgram: could not create pipe");
3429         delete[] paramBuf;
3430         return false;
3431         } 
3432     SetHandleInformation(stderrRead, HANDLE_FLAG_INHERIT, 0);
3434     // Create the process
3435     STARTUPINFO siStartupInfo;
3436     PROCESS_INFORMATION piProcessInfo;
3437     memset(&siStartupInfo, 0, sizeof(siStartupInfo));
3438     memset(&piProcessInfo, 0, sizeof(piProcessInfo));
3439     siStartupInfo.cb = sizeof(siStartupInfo);
3440     siStartupInfo.hStdError   =  stderrWrite;
3441     siStartupInfo.hStdOutput  =  stdoutWrite;
3442     siStartupInfo.hStdInput   =  stdinRead;
3443     siStartupInfo.dwFlags    |=  STARTF_USESTDHANDLES;
3444    
3445     if (!CreateProcess(NULL, paramBuf, NULL, NULL, true,
3446                 0, NULL, NULL, &siStartupInfo,
3447                 &piProcessInfo))
3448         {
3449         error("executeCommand : could not create process : %s",
3450                     win32LastError().c_str());
3451         ret = false;
3452         }
3454     delete[] paramBuf;
3456     DWORD bytesWritten;
3457     if (inbuf.size()>0 &&
3458         !WriteFile(stdinWrite, inbuf.c_str(), inbuf.size(), 
3459                &bytesWritten, NULL))
3460         {
3461         error("executeCommand: could not write to pipe");
3462         return false;
3463         }    
3464     if (!CloseHandle(stdinWrite))
3465         {          
3466         error("executeCommand: could not close write pipe");
3467         return false;
3468         }
3469     if (!CloseHandle(stdoutWrite))
3470         {
3471         error("executeCommand: could not close read pipe");
3472         return false;
3473         }
3474     if (!CloseHandle(stderrWrite))
3475         {
3476         error("executeCommand: could not close read pipe");
3477         return false;
3478         }
3480     bool lastLoop = false;
3481     while (true)
3482         {
3483         DWORD avail;
3484         DWORD bytesRead;
3485         char readBuf[4096];
3487         //trace("## stderr");
3488         PeekNamedPipe(stderrRead, NULL, 0, NULL, &avail, NULL);
3489         if (avail > 0)
3490             {
3491             bytesRead = 0;
3492             if (avail>4096) avail = 4096;
3493             ReadFile(stderrRead, readBuf, avail, &bytesRead, NULL);
3494             if (bytesRead > 0)
3495                 {
3496                 for (unsigned int i=0 ; i<bytesRead ; i++)
3497                     errbuf.push_back(readBuf[i]);
3498                 }
3499             }
3501         //trace("## stdout");
3502         PeekNamedPipe(stdoutRead, NULL, 0, NULL, &avail, NULL);
3503         if (avail > 0)
3504             {
3505             bytesRead = 0;
3506             if (avail>4096) avail = 4096;
3507             ReadFile(stdoutRead, readBuf, avail, &bytesRead, NULL);
3508             if (bytesRead > 0)
3509                 {
3510                 for (unsigned int i=0 ; i<bytesRead ; i++)
3511                     outbuf.push_back(readBuf[i]);
3512                 }
3513             }
3514             
3515         //Was this the final check after program done?
3516         if (lastLoop)
3517             break;
3519         DWORD exitCode;
3520         GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
3521         if (exitCode != STILL_ACTIVE)
3522             lastLoop = true;
3524         Sleep(50);
3525         }    
3526     //trace("outbuf:%s", outbuf.c_str());
3527     if (!CloseHandle(stdoutRead))
3528         {
3529         error("executeCommand: could not close read pipe");
3530         return false;
3531         }
3532     if (!CloseHandle(stderrRead))
3533         {
3534         error("executeCommand: could not close read pipe");
3535         return false;
3536         }
3538     DWORD exitCode;
3539     GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
3540     //trace("exit code:%d", exitCode);
3541     if (exitCode != 0)
3542         {
3543         ret = false;
3544         }
3545     
3546     CloseHandle(piProcessInfo.hProcess);
3547     CloseHandle(piProcessInfo.hThread);
3549     return ret;
3551 #else //do it unix-style
3553     String s;
3554     FILE *f = popen(command.c_str(), "r");
3555     int errnum = 0;
3556     if (f)
3557         {
3558         while (true)
3559             {
3560             int ch = fgetc(f);
3561             if (ch < 0)
3562                 break;
3563             s.push_back((char)ch);
3564             }
3565         errnum = pclose(f);
3566         }
3567     outbuf = s;
3568     if (errnum != 0)
3569         {
3570         error("exec of command '%s' failed : %s",
3571              command.c_str(), strerror(errno));
3572         return false;
3573         }
3574     else
3575         return true;
3577 #endif
3578
3583 bool MakeBase::listDirectories(const String &baseName,
3584                               const String &dirName,
3585                               std::vector<String> &res)
3587     res.push_back(dirName);
3588     String fullPath = baseName;
3589     if (dirName.size()>0)
3590         {
3591         fullPath.append("/");
3592         fullPath.append(dirName);
3593         }
3594     DIR *dir = opendir(fullPath.c_str());
3595     while (true)
3596         {
3597         struct dirent *de = readdir(dir);
3598         if (!de)
3599             break;
3601         //Get the directory member name
3602         String s = de->d_name;
3603         if (s.size() == 0 || s[0] == '.')
3604             continue;
3605         String childName = dirName;
3606         childName.append("/");
3607         childName.append(s);
3609         String fullChildPath = baseName;
3610         fullChildPath.append("/");
3611         fullChildPath.append(childName);
3612         struct stat finfo;
3613         String childNative = getNativePath(fullChildPath);
3614         if (stat(childNative.c_str(), &finfo)<0)
3615             {
3616             error("cannot stat file:%s", childNative.c_str());
3617             }
3618         else if (S_ISDIR(finfo.st_mode))
3619             {
3620             //trace("directory: %s", childName.c_str());
3621             if (!listDirectories(baseName, childName, res))
3622                 return false;
3623             }
3624         }
3625     closedir(dir);
3627     return true;
3631 bool MakeBase::listFiles(const String &baseDir,
3632                          const String &dirName,
3633                          std::vector<String> &res)
3635     String fullDir = baseDir;
3636     if (dirName.size()>0)
3637         {
3638         fullDir.append("/");
3639         fullDir.append(dirName);
3640         }
3641     String dirNative = getNativePath(fullDir);
3643     std::vector<String> subdirs;
3644     DIR *dir = opendir(dirNative.c_str());
3645     if (!dir)
3646         {
3647         error("Could not open directory %s : %s",
3648               dirNative.c_str(), strerror(errno));
3649         return false;
3650         }
3651     while (true)
3652         {
3653         struct dirent *de = readdir(dir);
3654         if (!de)
3655             break;
3657         //Get the directory member name
3658         String s = de->d_name;
3659         if (s.size() == 0 || s[0] == '.')
3660             continue;
3661         String childName;
3662         if (dirName.size()>0)
3663             {
3664             childName.append(dirName);
3665             childName.append("/");
3666             }
3667         childName.append(s);
3668         String fullChild = baseDir;
3669         fullChild.append("/");
3670         fullChild.append(childName);
3671         
3672         if (isDirectory(fullChild))
3673             {
3674             //trace("directory: %s", childName.c_str());
3675             if (!listFiles(baseDir, childName, res))
3676                 return false;
3677             continue;
3678             }
3679         else if (!isRegularFile(fullChild))
3680             {
3681             error("unknown file:%s", childName.c_str());
3682             return false;
3683             }
3685        //all done!
3686         res.push_back(childName);
3688         }
3689     closedir(dir);
3691     return true;
3695 bool MakeBase::listFiles(MakeBase &propRef, FileSet &fileSet)
3697     String baseDir = propRef.resolve(fileSet.getDirectory());
3698     std::vector<String> fileList;
3699     if (!listFiles(baseDir, "", fileList))
3700         return false;
3702     std::vector<String> includes = fileSet.getIncludes();
3703     std::vector<String> excludes = fileSet.getExcludes();
3705     std::vector<String> incs;
3706     std::vector<String>::iterator iter;
3708     std::sort(fileList.begin(), fileList.end());
3710     //If there are <includes>, then add files to the output
3711     //in the order of the include list
3712     if (includes.size()==0)
3713         incs = fileList;
3714     else
3715         {
3716         for (iter = includes.begin() ; iter != includes.end() ; iter++)
3717             {
3718             String pattern = *iter;
3719             std::vector<String>::iterator siter;
3720             for (siter = fileList.begin() ; siter != fileList.end() ; siter++)
3721                 {
3722                 String s = *siter;
3723                 if (regexMatch(s, pattern))
3724                     {
3725                     //trace("INCLUDED:%s", s.c_str());
3726                     incs.push_back(s);
3727                     }
3728                 }
3729             }
3730         }
3732     //Now trim off the <excludes>
3733     std::vector<String> res;
3734     for (iter = incs.begin() ; iter != incs.end() ; iter++)
3735         {
3736         String s = *iter;
3737         bool skipme = false;
3738         std::vector<String>::iterator siter;
3739         for (siter = excludes.begin() ; siter != excludes.end() ; siter++)
3740             {
3741             String pattern = *siter;
3742             if (regexMatch(s, pattern))
3743                 {
3744                 //trace("EXCLUDED:%s", s.c_str());
3745                 skipme = true;
3746                 break;
3747                 }
3748             }
3749         if (!skipme)
3750             res.push_back(s);
3751         }
3752         
3753     fileSet.setFiles(res);
3755     return true;
3762 bool MakeBase::getSubstitutions(const String &str, String &result)
3764     String s = trim(str);
3765     int len = (int)s.size();
3766     String val;
3767     for (int i=0 ; i<len ; i++)
3768         {
3769         char ch = s[i];
3770         if (ch == '$' && s[i+1] == '{')
3771             {
3772             String varname;
3773             int j = i+2;
3774             for ( ; j<len ; j++)
3775                 {
3776                 ch = s[j];
3777                 if (ch == '$' && s[j+1] == '{')
3778                     {
3779                     error("attribute %s cannot have nested variable references",
3780                            s.c_str());
3781                     return false;
3782                     }
3783                 else if (ch == '}')
3784                     {
3785                     std::map<String, String>::iterator iter;
3786                     iter = properties.find(trim(varname));
3787                     if (iter != properties.end())
3788                         {
3789                         val.append(iter->second);
3790                         }
3791                     else
3792                         {
3793                         error("property ${%s} not found", varname.c_str());
3794                         return false;
3795                         }
3796                     break;
3797                     }
3798                 else
3799                     {
3800                     varname.push_back(ch);
3801                     }
3802                 }
3803             i = j;
3804             }
3805         else
3806             {
3807             val.push_back(ch);
3808             }
3809         }
3810     result = val;
3811     return true;
3815 bool MakeBase::getAttribute(Element *elem, const String &name,
3816                                     String &result)
3818     String s = elem->getAttribute(name);
3819     return getSubstitutions(s, result);
3823 bool MakeBase::getValue(Element *elem, String &result)
3825     String s = elem->getValue();
3826     //Replace all runs of whitespace with a single space
3827     return getSubstitutions(s, result);
3831 /**
3832  * Turn 'true' and 'false' into boolean values
3833  */             
3834 bool MakeBase::getBool(const String &str, bool &val)
3836     if (str == "true")
3837         val = true;
3838     else if (str == "false")
3839         val = false;
3840     else
3841         {
3842         error("expected 'true' or 'false'.  found '%s'", str.c_str());
3843         return false;
3844         }
3845     return true;
3851 /**
3852  * Parse a <patternset> entry
3853  */  
3854 bool MakeBase::parsePatternSet(Element *elem,
3855                           MakeBase &propRef,
3856                           std::vector<String> &includes,
3857                           std::vector<String> &excludes
3858                           )
3860     std::vector<Element *> children  = elem->getChildren();
3861     for (unsigned int i=0 ; i<children.size() ; i++)
3862         {
3863         Element *child = children[i];
3864         String tagName = child->getName();
3865         if (tagName == "exclude")
3866             {
3867             String fname;
3868             if (!propRef.getAttribute(child, "name", fname))
3869                 return false;
3870             //trace("EXCLUDE: %s", fname.c_str());
3871             excludes.push_back(fname);
3872             }
3873         else if (tagName == "include")
3874             {
3875             String fname;
3876             if (!propRef.getAttribute(child, "name", fname))
3877                 return false;
3878             //trace("INCLUDE: %s", fname.c_str());
3879             includes.push_back(fname);
3880             }
3881         }
3883     return true;
3889 /**
3890  * Parse a <fileset> entry, and determine which files
3891  * should be included
3892  */  
3893 bool MakeBase::parseFileSet(Element *elem,
3894                           MakeBase &propRef,
3895                           FileSet &fileSet)
3897     String name = elem->getName();
3898     if (name != "fileset")
3899         {
3900         error("expected <fileset>");
3901         return false;
3902         }
3905     std::vector<String> includes;
3906     std::vector<String> excludes;
3908     //A fileset has one implied patternset
3909     if (!parsePatternSet(elem, propRef, includes, excludes))
3910         {
3911         return false;
3912         }
3913     //Look for child tags, including more patternsets
3914     std::vector<Element *> children  = elem->getChildren();
3915     for (unsigned int i=0 ; i<children.size() ; i++)
3916         {
3917         Element *child = children[i];
3918         String tagName = child->getName();
3919         if (tagName == "patternset")
3920             {
3921             if (!parsePatternSet(child, propRef, includes, excludes))
3922                 {
3923                 return false;
3924                 }
3925             }
3926         }
3928     String dir;
3929     //Now do the stuff
3930     //Get the base directory for reading file names
3931     if (!propRef.getAttribute(elem, "dir", dir))
3932         return false;
3934     fileSet.setDirectory(dir);
3935     fileSet.setIncludes(includes);
3936     fileSet.setExcludes(excludes);
3937     
3938     /*
3939     std::vector<String> fileList;
3940     if (dir.size() > 0)
3941         {
3942         String baseDir = propRef.resolve(dir);
3943         if (!listFiles(baseDir, "", includes, excludes, fileList))
3944             return false;
3945         }
3946     std::sort(fileList.begin(), fileList.end());
3947     result = fileList;
3948     */
3950     
3951     /*
3952     for (unsigned int i=0 ; i<result.size() ; i++)
3953         {
3954         trace("RES:%s", result[i].c_str());
3955         }
3956     */
3958     
3959     return true;
3964 /**
3965  * Create a directory, making intermediate dirs
3966  * if necessary
3967  */                  
3968 bool MakeBase::createDirectory(const String &dirname)
3970     //trace("## createDirectory: %s", dirname.c_str());
3971     //## first check if it exists
3972     struct stat finfo;
3973     String nativeDir = getNativePath(dirname);
3974     char *cnative = (char *) nativeDir.c_str();
3975 #ifdef __WIN32__
3976     if (strlen(cnative)==2 && cnative[1]==':')
3977         return true;
3978 #endif
3979     if (stat(cnative, &finfo)==0)
3980         {
3981         if (!S_ISDIR(finfo.st_mode))
3982             {
3983             error("mkdir: file %s exists but is not a directory",
3984                   cnative);
3985             return false;
3986             }
3987         else //exists
3988             {
3989             return true;
3990             }
3991         }
3993     //## 2: pull off the last path segment, if any,
3994     //## to make the dir 'above' this one, if necessary
3995     unsigned int pos = dirname.find_last_of('/');
3996     if (pos>0 && pos != dirname.npos)
3997         {
3998         String subpath = dirname.substr(0, pos);
3999         //A letter root (c:) ?
4000         if (!createDirectory(subpath))
4001             return false;
4002         }
4003         
4004     //## 3: now make
4005 #ifdef __WIN32__
4006     if (mkdir(cnative)<0)
4007 #else
4008     if (mkdir(cnative, S_IRWXU | S_IRWXG | S_IRWXO)<0)
4009 #endif
4010         {
4011         error("cannot make directory '%s' : %s",
4012                  cnative, strerror(errno));
4013         return false;
4014         }
4015         
4016     return true;
4020 /**
4021  * Remove a directory recursively
4022  */ 
4023 bool MakeBase::removeDirectory(const String &dirName)
4025     char *dname = (char *)dirName.c_str();
4027     DIR *dir = opendir(dname);
4028     if (!dir)
4029         {
4030         //# Let this fail nicely.
4031         return true;
4032         //error("error opening directory %s : %s", dname, strerror(errno));
4033         //return false;
4034         }
4035     
4036     while (true)
4037         {
4038         struct dirent *de = readdir(dir);
4039         if (!de)
4040             break;
4042         //Get the directory member name
4043         String s = de->d_name;
4044         if (s.size() == 0 || s[0] == '.')
4045             continue;
4046         String childName;
4047         if (dirName.size() > 0)
4048             {
4049             childName.append(dirName);
4050             childName.append("/");
4051             }
4052         childName.append(s);
4055         struct stat finfo;
4056         String childNative = getNativePath(childName);
4057         char *cnative = (char *)childNative.c_str();
4058         if (stat(cnative, &finfo)<0)
4059             {
4060             error("cannot stat file:%s", cnative);
4061             }
4062         else if (S_ISDIR(finfo.st_mode))
4063             {
4064             //trace("DEL dir: %s", childName.c_str());
4065             if (!removeDirectory(childName))
4066                 {
4067                 return false;
4068                 }
4069             }
4070         else if (!S_ISREG(finfo.st_mode))
4071             {
4072             //trace("not regular: %s", cnative);
4073             }
4074         else
4075             {
4076             //trace("DEL file: %s", childName.c_str());
4077             if (remove(cnative)<0)
4078                 {
4079                 error("error deleting %s : %s",
4080                      cnative, strerror(errno));
4081                 return false;
4082                 }
4083             }
4084         }
4085     closedir(dir);
4087     //Now delete the directory
4088     String native = getNativePath(dirName);
4089     if (rmdir(native.c_str())<0)
4090         {
4091         error("could not delete directory %s : %s",
4092             native.c_str() , strerror(errno));
4093         return false;
4094         }
4096     return true;
4097     
4101 /**
4102  * Copy a file from one name to another. Perform only if needed
4103  */ 
4104 bool MakeBase::copyFile(const String &srcFile, const String &destFile)
4106     //# 1 Check up-to-date times
4107     String srcNative = getNativePath(srcFile);
4108     struct stat srcinfo;
4109     if (stat(srcNative.c_str(), &srcinfo)<0)
4110         {
4111         error("source file %s for copy does not exist",
4112                  srcNative.c_str());
4113         return false;
4114         }
4116     String destNative = getNativePath(destFile);
4117     struct stat destinfo;
4118     if (stat(destNative.c_str(), &destinfo)==0)
4119         {
4120         if (destinfo.st_mtime >= srcinfo.st_mtime)
4121             return true;
4122         }
4123         
4124     //# 2 prepare a destination directory if necessary
4125     unsigned int pos = destFile.find_last_of('/');
4126     if (pos != destFile.npos)
4127         {
4128         String subpath = destFile.substr(0, pos);
4129         if (!createDirectory(subpath))
4130             return false;
4131         }
4133     //# 3 do the data copy
4134 #ifndef __WIN32__
4136     FILE *srcf = fopen(srcNative.c_str(), "rb");
4137     if (!srcf)
4138         {
4139         error("copyFile cannot open '%s' for reading", srcNative.c_str());
4140         return false;
4141         }
4142     FILE *destf = fopen(destNative.c_str(), "wb");
4143     if (!destf)
4144         {
4145         error("copyFile cannot open %s for writing", srcNative.c_str());
4146         return false;
4147         }
4149     while (!feof(srcf))
4150         {
4151         int ch = fgetc(srcf);
4152         if (ch<0)
4153             break;
4154         fputc(ch, destf);
4155         }
4157     fclose(destf);
4158     fclose(srcf);
4160 #else
4161     
4162     if (!CopyFile(srcNative.c_str(), destNative.c_str(), false))
4163         {
4164         error("copyFile from %s to %s failed",
4165              srcNative.c_str(), destNative.c_str());
4166         return false;
4167         }
4168         
4169 #endif /* __WIN32__ */
4172     return true;
4177 /**
4178  * Tests if the file exists and is a regular file
4179  */ 
4180 bool MakeBase::isRegularFile(const String &fileName)
4182     String native = getNativePath(fileName);
4183     struct stat finfo;
4184     
4185     //Exists?
4186     if (stat(native.c_str(), &finfo)<0)
4187         return false;
4190     //check the file mode
4191     if (!S_ISREG(finfo.st_mode))
4192         return false;
4194     return true;
4197 /**
4198  * Tests if the file exists and is a directory
4199  */ 
4200 bool MakeBase::isDirectory(const String &fileName)
4202     String native = getNativePath(fileName);
4203     struct stat finfo;
4204     
4205     //Exists?
4206     if (stat(native.c_str(), &finfo)<0)
4207         return false;
4210     //check the file mode
4211     if (!S_ISDIR(finfo.st_mode))
4212         return false;
4214     return true;
4219 /**
4220  * Tests is the modification of fileA is newer than fileB
4221  */ 
4222 bool MakeBase::isNewerThan(const String &fileA, const String &fileB)
4224     //trace("isNewerThan:'%s' , '%s'", fileA.c_str(), fileB.c_str());
4225     String nativeA = getNativePath(fileA);
4226     struct stat infoA;
4227     //IF source does not exist, NOT newer
4228     if (stat(nativeA.c_str(), &infoA)<0)
4229         {
4230         return false;
4231         }
4233     String nativeB = getNativePath(fileB);
4234     struct stat infoB;
4235     //IF dest does not exist, YES, newer
4236     if (stat(nativeB.c_str(), &infoB)<0)
4237         {
4238         return true;
4239         }
4241     //check the actual times
4242     if (infoA.st_mtime > infoB.st_mtime)
4243         {
4244         return true;
4245         }
4247     return false;
4251 //########################################################################
4252 //# P K G    C O N F I G
4253 //########################################################################
4255 /**
4256  *
4257  */
4258 class PkgConfig : public MakeBase
4261 public:
4263     /**
4264      *
4265      */
4266     PkgConfig()
4267         { init(); }
4269     /**
4270      *
4271      */
4272     PkgConfig(const String &namearg)
4273         { init(); name = namearg; }
4275     /**
4276      *
4277      */
4278     PkgConfig(const PkgConfig &other)
4279         { assign(other); }
4281     /**
4282      *
4283      */
4284     PkgConfig &operator=(const PkgConfig &other)
4285         { assign(other); return *this; }
4287     /**
4288      *
4289      */
4290     virtual ~PkgConfig()
4291         { }
4293     /**
4294      *
4295      */
4296     virtual String getName()
4297         { return name; }
4299     /**
4300      *
4301      */
4302     virtual String getDescription()
4303         { return description; }
4305     /**
4306      *
4307      */
4308     virtual String getCflags()
4309         { return cflags; }
4311     /**
4312      *
4313      */
4314     virtual String getLibs()
4315         { return libs; }
4317     /**
4318      *
4319      */
4320     virtual String getVersion()
4321         { return version; }
4323     /**
4324      *
4325      */
4326     virtual int getMajorVersion()
4327         { return majorVersion; }
4329     /**
4330      *
4331      */
4332     virtual int getMinorVersion()
4333         { return minorVersion; }
4335     /**
4336      *
4337      */
4338     virtual int getMicroVersion()
4339         { return microVersion; }
4341     /**
4342      *
4343      */
4344     virtual std::map<String, String> &getAttributes()
4345         { return attrs; }
4347     /**
4348      *
4349      */
4350     virtual std::vector<String> &getRequireList()
4351         { return requireList; }
4353     virtual bool readFile(const String &fileName);
4355 private:
4357     void init()
4358         {
4359         name         = "";
4360         description  = "";
4361         cflags       = "";
4362         libs         = "";
4363         requires     = "";
4364         version      = "";
4365         majorVersion = 0;
4366         minorVersion = 0;
4367         microVersion = 0;
4368         fileName     = "";
4369         attrs.clear();
4370         requireList.clear();
4371         }
4373     void assign(const PkgConfig &other)
4374         {
4375         name         = other.name;
4376         description  = other.description;
4377         cflags       = other.cflags;
4378         libs         = other.libs;
4379         requires     = other.requires;
4380         version      = other.version;
4381         majorVersion = other.majorVersion;
4382         minorVersion = other.minorVersion;
4383         microVersion = other.microVersion;
4384         fileName     = other.fileName;
4385         attrs        = other.attrs;
4386         requireList  = other.requireList;
4387         }
4391     int get(int pos);
4393     int skipwhite(int pos);
4395     int getword(int pos, String &ret);
4397     void parseRequires();
4399     void parseVersion();
4401     bool parse(const String &buf);
4403     void dumpAttrs();
4405     String name;
4407     String description;
4409     String cflags;
4411     String libs;
4413     String requires;
4415     String version;
4417     int majorVersion;
4419     int minorVersion;
4421     int microVersion;
4423     String fileName;
4425     std::map<String, String> attrs;
4427     std::vector<String> requireList;
4429     char *parsebuf;
4430     int parselen;
4431 };
4434 /**
4435  * Get a character from the buffer at pos.  If out of range,
4436  * return -1 for safety
4437  */
4438 int PkgConfig::get(int pos)
4440     if (pos>parselen)
4441         return -1;
4442     return parsebuf[pos];
4447 /**
4448  *  Skip over all whitespace characters beginning at pos.  Return
4449  *  the position of the first non-whitespace character.
4450  */
4451 int PkgConfig::skipwhite(int pos)
4453     while (pos < parselen)
4454         {
4455         int ch = get(pos);
4456         if (ch < 0)
4457             break;
4458         if (!isspace(ch))
4459             break;
4460         pos++;
4461         }
4462     return pos;
4466 /**
4467  *  Parse the buffer beginning at pos, for a word.  Fill
4468  *  'ret' with the result.  Return the position after the
4469  *  word.
4470  */
4471 int PkgConfig::getword(int pos, String &ret)
4473     while (pos < parselen)
4474         {
4475         int ch = get(pos);
4476         if (ch < 0)
4477             break;
4478         if (!isalnum(ch) && ch != '_' && ch != '-'&& ch != '.')
4479             break;
4480         ret.push_back((char)ch);
4481         pos++;
4482         }
4483     return pos;
4486 void PkgConfig::parseRequires()
4488     if (requires.size() == 0)
4489         return;
4490     parsebuf = (char *)requires.c_str();
4491     parselen = requires.size();
4492     int pos = 0;
4493     while (pos < parselen)
4494         {
4495         pos = skipwhite(pos);
4496         String val;
4497         int pos2 = getword(pos, val);
4498         if (pos2 == pos)
4499             break;
4500         pos = pos2;
4501         //trace("val %s", val.c_str());
4502         requireList.push_back(val);
4503         }
4506 static int getint(const String str)
4508     char *s = (char *)str.c_str();
4509     char *ends = NULL;
4510     long val = strtol(s, &ends, 10);
4511     if (ends == s)
4512         return 0L;
4513     else
4514         return val;
4517 void PkgConfig::parseVersion()
4519     if (version.size() == 0)
4520         return;
4521     String s1, s2, s3;
4522     unsigned int pos = 0;
4523     unsigned int pos2 = version.find('.', pos);
4524     if (pos2 == version.npos)
4525         {
4526         s1 = version;
4527         }
4528     else
4529         {
4530         s1 = version.substr(pos, pos2-pos);
4531         pos = pos2;
4532         pos++;
4533         if (pos < version.size())
4534             {
4535             pos2 = version.find('.', pos);
4536             if (pos2 == version.npos)
4537                 {
4538                 s2 = version.substr(pos, version.size()-pos);
4539                 }
4540             else
4541                 {
4542                 s2 = version.substr(pos, pos2-pos);
4543                 pos = pos2;
4544                 pos++;
4545                 if (pos < version.size())
4546                     s3 = version.substr(pos, pos2-pos);
4547                 }
4548             }
4549         }
4551     majorVersion = getint(s1);
4552     minorVersion = getint(s2);
4553     microVersion = getint(s3);
4554     //trace("version:%d.%d.%d", majorVersion,
4555     //          minorVersion, microVersion );
4559 bool PkgConfig::parse(const String &buf)
4561     init();
4563     parsebuf = (char *)buf.c_str();
4564     parselen = buf.size();
4565     int pos = 0;
4568     while (pos < parselen)
4569         {
4570         String attrName;
4571         pos = skipwhite(pos);
4572         int ch = get(pos);
4573         if (ch == '#')
4574             {
4575             //comment.  eat the rest of the line
4576             while (pos < parselen)
4577                 {
4578                 ch = get(pos);
4579                 if (ch == '\n' || ch < 0)
4580                     break;
4581                 pos++;
4582                 }
4583             continue;
4584             }
4585         pos = getword(pos, attrName);
4586         if (attrName.size() == 0)
4587             continue;
4588         pos = skipwhite(pos);
4589         ch = get(pos);
4590         if (ch != ':' && ch != '=')
4591             {
4592             error("expected ':' or '='");
4593             return false;
4594             }
4595         pos++;
4596         pos = skipwhite(pos);
4597         String attrVal;
4598         while (pos < parselen)
4599             {
4600             ch = get(pos);
4601             if (ch == '\n' || ch < 0)
4602                 break;
4603             else if (ch == '$' && get(pos+1) == '{')
4604                 {
4605                 //#  this is a ${substitution}
4606                 pos += 2;
4607                 String subName;
4608                 while (pos < parselen)
4609                     {
4610                     ch = get(pos);
4611                     if (ch < 0)
4612                         {
4613                         error("unterminated substitution");
4614                         return false;
4615                         }
4616                     else if (ch == '}')
4617                         break;
4618                     else
4619                         subName.push_back((char)ch);
4620                     pos++;
4621                     }
4622                 //trace("subName:%s", subName.c_str());
4623                 String subVal = attrs[subName];
4624                 //trace("subVal:%s", subVal.c_str());
4625                 attrVal.append(subVal);
4626                 }
4627             else
4628                 attrVal.push_back((char)ch);
4629             pos++;
4630             }
4632         attrVal = trim(attrVal);
4633         attrs[attrName] = attrVal;
4635         if (attrName == "Name")
4636             name = attrVal;
4637         else if (attrName == "Description")
4638             description = attrVal;
4639         else if (attrName == "Cflags")
4640             cflags = attrVal;
4641         else if (attrName == "Libs")
4642             libs = attrVal;
4643         else if (attrName == "Requires")
4644             requires = attrVal;
4645         else if (attrName == "Version")
4646             version = attrVal;
4648         //trace("name:'%s'  value:'%s'",
4649         //      attrName.c_str(), attrVal.c_str());
4650         }
4653     parseRequires();
4654     parseVersion();
4656     return true;
4659 void PkgConfig::dumpAttrs()
4661     //trace("### PkgConfig attributes for %s", fileName.c_str());
4662     std::map<String, String>::iterator iter;
4663     for (iter=attrs.begin() ; iter!=attrs.end() ; iter++)
4664         {
4665         trace("   %s = %s", iter->first.c_str(), iter->second.c_str());
4666         }
4670 bool PkgConfig::readFile(const String &fileNameArg)
4672     fileName = fileNameArg;
4674     FILE *f = fopen(fileName.c_str(), "r");
4675     if (!f)
4676         {
4677         error("cannot open file '%s' for reading", fileName.c_str());
4678         return false;
4679         }
4680     String buf;
4681     while (true)
4682         {
4683         int ch = fgetc(f);
4684         if (ch < 0)
4685             break;
4686         buf.push_back((char)ch);
4687         }
4688     fclose(f);
4690     //trace("####### File:\n%s", buf.c_str());
4691     if (!parse(buf))
4692         {
4693         return false;
4694         }
4696     dumpAttrs();
4698     return true;
4705 //########################################################################
4706 //# D E P T O O L
4707 //########################################################################
4711 /**
4712  *  Class which holds information for each file.
4713  */
4714 class FileRec
4716 public:
4718     typedef enum
4719         {
4720         UNKNOWN,
4721         CFILE,
4722         HFILE,
4723         OFILE
4724         } FileType;
4726     /**
4727      *  Constructor
4728      */
4729     FileRec()
4730         {init(); type = UNKNOWN;}
4732     /**
4733      *  Copy constructor
4734      */
4735     FileRec(const FileRec &other)
4736         {init(); assign(other);}
4737     /**
4738      *  Constructor
4739      */
4740     FileRec(int typeVal)
4741         {init(); type = typeVal;}
4742     /**
4743      *  Assignment operator
4744      */
4745     FileRec &operator=(const FileRec &other)
4746         {init(); assign(other); return *this;}
4749     /**
4750      *  Destructor
4751      */
4752     ~FileRec()
4753         {}
4755     /**
4756      *  Directory part of the file name
4757      */
4758     String path;
4760     /**
4761      *  Base name, sans directory and suffix
4762      */
4763     String baseName;
4765     /**
4766      *  File extension, such as cpp or h
4767      */
4768     String suffix;
4770     /**
4771      *  Type of file: CFILE, HFILE, OFILE
4772      */
4773     int type;
4775     /**
4776      * Used to list files ref'd by this one
4777      */
4778     std::map<String, FileRec *> files;
4781 private:
4783     void init()
4784         {
4785         }
4787     void assign(const FileRec &other)
4788         {
4789         type     = other.type;
4790         baseName = other.baseName;
4791         suffix   = other.suffix;
4792         files    = other.files;
4793         }
4795 };
4799 /**
4800  *  Simpler dependency record
4801  */
4802 class DepRec
4804 public:
4806     /**
4807      *  Constructor
4808      */
4809     DepRec()
4810         {init();}
4812     /**
4813      *  Copy constructor
4814      */
4815     DepRec(const DepRec &other)
4816         {init(); assign(other);}
4817     /**
4818      *  Constructor
4819      */
4820     DepRec(const String &fname)
4821         {init(); name = fname; }
4822     /**
4823      *  Assignment operator
4824      */
4825     DepRec &operator=(const DepRec &other)
4826         {init(); assign(other); return *this;}
4829     /**
4830      *  Destructor
4831      */
4832     ~DepRec()
4833         {}
4835     /**
4836      *  Directory part of the file name
4837      */
4838     String path;
4840     /**
4841      *  Base name, without the path and suffix
4842      */
4843     String name;
4845     /**
4846      *  Suffix of the source
4847      */
4848     String suffix;
4851     /**
4852      * Used to list files ref'd by this one
4853      */
4854     std::vector<String> files;
4857 private:
4859     void init()
4860         {
4861         }
4863     void assign(const DepRec &other)
4864         {
4865         path     = other.path;
4866         name     = other.name;
4867         suffix   = other.suffix;
4868         files    = other.files;
4869         }
4871 };
4874 class DepTool : public MakeBase
4876 public:
4878     /**
4879      *  Constructor
4880      */
4881     DepTool()
4882         {init();}
4884     /**
4885      *  Copy constructor
4886      */
4887     DepTool(const DepTool &other)
4888         {init(); assign(other);}
4890     /**
4891      *  Assignment operator
4892      */
4893     DepTool &operator=(const DepTool &other)
4894         {init(); assign(other); return *this;}
4897     /**
4898      *  Destructor
4899      */
4900     ~DepTool()
4901         {}
4904     /**
4905      *  Reset this section of code
4906      */
4907     virtual void init();
4908     
4909     /**
4910      *  Reset this section of code
4911      */
4912     virtual void assign(const DepTool &other)
4913         {
4914         }
4915     
4916     /**
4917      *  Sets the source directory which will be scanned
4918      */
4919     virtual void setSourceDirectory(const String &val)
4920         { sourceDir = val; }
4922     /**
4923      *  Returns the source directory which will be scanned
4924      */
4925     virtual String getSourceDirectory()
4926         { return sourceDir; }
4928     /**
4929      *  Sets the list of files within the directory to analyze
4930      */
4931     virtual void setFileList(const std::vector<String> &list)
4932         { fileList = list; }
4934     /**
4935      * Creates the list of all file names which will be
4936      * candidates for further processing.  Reads make.exclude
4937      * to see which files for directories to leave out.
4938      */
4939     virtual bool createFileList();
4942     /**
4943      *  Generates the forward dependency list
4944      */
4945     virtual bool generateDependencies();
4948     /**
4949      *  Generates the forward dependency list, saving the file
4950      */
4951     virtual bool generateDependencies(const String &);
4954     /**
4955      *  Load a dependency file
4956      */
4957     std::vector<DepRec> loadDepFile(const String &fileName);
4959     /**
4960      *  Load a dependency file, generating one if necessary
4961      */
4962     std::vector<DepRec> getDepFile(const String &fileName,
4963               bool forceRefresh);
4965     /**
4966      *  Save a dependency file
4967      */
4968     bool saveDepFile(const String &fileName);
4971 private:
4974     /**
4975      *
4976      */
4977     void parseName(const String &fullname,
4978                    String &path,
4979                    String &basename,
4980                    String &suffix);
4982     /**
4983      *
4984      */
4985     int get(int pos);
4987     /**
4988      *
4989      */
4990     int skipwhite(int pos);
4992     /**
4993      *
4994      */
4995     int getword(int pos, String &ret);
4997     /**
4998      *
4999      */
5000     bool sequ(int pos, char *key);
5002     /**
5003      *
5004      */
5005     bool addIncludeFile(FileRec *frec, const String &fname);
5007     /**
5008      *
5009      */
5010     bool scanFile(const String &fname, FileRec *frec);
5012     /**
5013      *
5014      */
5015     bool processDependency(FileRec *ofile,
5016                            FileRec *include,
5017                            int depth);
5019     /**
5020      *
5021      */
5022     String sourceDir;
5024     /**
5025      *
5026      */
5027     std::vector<String> fileList;
5029     /**
5030      *
5031      */
5032     std::vector<String> directories;
5034     /**
5035      * A list of all files which will be processed for
5036      * dependencies.  This is the only list that has the actual
5037      * records.  All other lists have pointers to these records.     
5038      */
5039     std::map<String, FileRec *> allFiles;
5041     /**
5042      * The list of .o files, and the
5043      * dependencies upon them.
5044      */
5045     std::map<String, FileRec *> depFiles;
5047     int depFileSize;
5048     char *depFileBuf;
5050     static const int readBufSize = 8192;
5051     char readBuf[8193];//byte larger
5053 };
5059 /**
5060  *  Clean up after processing.  Called by the destructor, but should
5061  *  also be called before the object is reused.
5062  */
5063 void DepTool::init()
5065     sourceDir = ".";
5067     fileList.clear();
5068     directories.clear();
5069     
5070     //clear refs
5071     depFiles.clear();
5072     //clear records
5073     std::map<String, FileRec *>::iterator iter;
5074     for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5075          delete iter->second;
5077     allFiles.clear(); 
5084 /**
5085  *  Parse a full path name into path, base name, and suffix
5086  */
5087 void DepTool::parseName(const String &fullname,
5088                         String &path,
5089                         String &basename,
5090                         String &suffix)
5092     if (fullname.size() < 2)
5093         return;
5095     unsigned int pos = fullname.find_last_of('/');
5096     if (pos != fullname.npos && pos<fullname.size()-1)
5097         {
5098         path = fullname.substr(0, pos);
5099         pos++;
5100         basename = fullname.substr(pos, fullname.size()-pos);
5101         }
5102     else
5103         {
5104         path = "";
5105         basename = fullname;
5106         }
5108     pos = basename.find_last_of('.');
5109     if (pos != basename.npos && pos<basename.size()-1)
5110         {
5111         suffix   = basename.substr(pos+1, basename.size()-pos-1);
5112         basename = basename.substr(0, pos);
5113         }
5115     //trace("parsename:%s %s %s", path.c_str(),
5116     //        basename.c_str(), suffix.c_str()); 
5121 /**
5122  *  Generate our internal file list.
5123  */
5124 bool DepTool::createFileList()
5127     for (unsigned int i=0 ; i<fileList.size() ; i++)
5128         {
5129         String fileName = fileList[i];
5130         //trace("## FileName:%s", fileName.c_str());
5131         String path;
5132         String basename;
5133         String sfx;
5134         parseName(fileName, path, basename, sfx);
5135         if (sfx == "cpp" || sfx == "c" || sfx == "cxx"   ||
5136             sfx == "cc" || sfx == "CC")
5137             {
5138             FileRec *fe         = new FileRec(FileRec::CFILE);
5139             fe->path            = path;
5140             fe->baseName        = basename;
5141             fe->suffix          = sfx;
5142             allFiles[fileName]  = fe;
5143             }
5144         else if (sfx == "h"   ||  sfx == "hh"  ||
5145                  sfx == "hpp" ||  sfx == "hxx")
5146             {
5147             FileRec *fe         = new FileRec(FileRec::HFILE);
5148             fe->path            = path;
5149             fe->baseName        = basename;
5150             fe->suffix          = sfx;
5151             allFiles[fileName]  = fe;
5152             }
5153         }
5155     if (!listDirectories(sourceDir, "", directories))
5156         return false;
5157         
5158     return true;
5165 /**
5166  * Get a character from the buffer at pos.  If out of range,
5167  * return -1 for safety
5168  */
5169 int DepTool::get(int pos)
5171     if (pos>depFileSize)
5172         return -1;
5173     return depFileBuf[pos];
5178 /**
5179  *  Skip over all whitespace characters beginning at pos.  Return
5180  *  the position of the first non-whitespace character.
5181  */
5182 int DepTool::skipwhite(int pos)
5184     while (pos < depFileSize)
5185         {
5186         int ch = get(pos);
5187         if (ch < 0)
5188             break;
5189         if (!isspace(ch))
5190             break;
5191         pos++;
5192         }
5193     return pos;
5197 /**
5198  *  Parse the buffer beginning at pos, for a word.  Fill
5199  *  'ret' with the result.  Return the position after the
5200  *  word.
5201  */
5202 int DepTool::getword(int pos, String &ret)
5204     while (pos < depFileSize)
5205         {
5206         int ch = get(pos);
5207         if (ch < 0)
5208             break;
5209         if (isspace(ch))
5210             break;
5211         ret.push_back((char)ch);
5212         pos++;
5213         }
5214     return pos;
5217 /**
5218  * Return whether the sequence of characters in the buffer
5219  * beginning at pos match the key,  for the length of the key
5220  */
5221 bool DepTool::sequ(int pos, char *key)
5223     while (*key)
5224         {
5225         if (*key != get(pos))
5226             return false;
5227         key++; pos++;
5228         }
5229     return true;
5234 /**
5235  *  Add an include file name to a file record.  If the name
5236  *  is not found in allFiles explicitly, try prepending include
5237  *  directory names to it and try again.
5238  */
5239 bool DepTool::addIncludeFile(FileRec *frec, const String &iname)
5242     std::map<String, FileRec *>::iterator iter =
5243            allFiles.find(iname);
5244     if (iter != allFiles.end()) //already exists
5245         {
5246          //h file in same dir
5247         FileRec *other = iter->second;
5248         //trace("local: '%s'", iname.c_str());
5249         frec->files[iname] = other;
5250         return true;
5251         }
5252     else 
5253         {
5254         //look in other dirs
5255         std::vector<String>::iterator diter;
5256         for (diter=directories.begin() ;
5257              diter!=directories.end() ; diter++)
5258             {
5259             String dfname = *diter;
5260             dfname.append("/");
5261             dfname.append(iname);
5262             iter = allFiles.find(dfname);
5263             if (iter != allFiles.end())
5264                 {
5265                 FileRec *other = iter->second;
5266                 //trace("other: '%s'", iname.c_str());
5267                 frec->files[dfname] = other;
5268                 return true;
5269                 }
5270             }
5271         }
5272     return true;
5277 /**
5278  *  Lightly parse a file to find the #include directives.  Do
5279  *  a bit of state machine stuff to make sure that the directive
5280  *  is valid.  (Like not in a comment).
5281  */
5282 bool DepTool::scanFile(const String &fname, FileRec *frec)
5284     String fileName;
5285     if (sourceDir.size() > 0)
5286         {
5287         fileName.append(sourceDir);
5288         fileName.append("/");
5289         }
5290     fileName.append(fname);
5291     String nativeName = getNativePath(fileName);
5292     FILE *f = fopen(nativeName.c_str(), "r");
5293     if (!f)
5294         {
5295         error("Could not open '%s' for reading", fname.c_str());
5296         return false;
5297         }
5298     String buf;
5299     while (!feof(f))
5300         {
5301         int len = fread(readBuf, 1, readBufSize, f);
5302         readBuf[len] = '\0';
5303         buf.append(readBuf);
5304         }
5305     fclose(f);
5307     depFileSize = buf.size();
5308     depFileBuf  = (char *)buf.c_str();
5309     int pos = 0;
5312     while (pos < depFileSize)
5313         {
5314         //trace("p:%c", get(pos));
5316         //# Block comment
5317         if (get(pos) == '/' && get(pos+1) == '*')
5318             {
5319             pos += 2;
5320             while (pos < depFileSize)
5321                 {
5322                 if (get(pos) == '*' && get(pos+1) == '/')
5323                     {
5324                     pos += 2;
5325                     break;
5326                     }
5327                 else
5328                     pos++;
5329                 }
5330             }
5331         //# Line comment
5332         else if (get(pos) == '/' && get(pos+1) == '/')
5333             {
5334             pos += 2;
5335             while (pos < depFileSize)
5336                 {
5337                 if (get(pos) == '\n')
5338                     {
5339                     pos++;
5340                     break;
5341                     }
5342                 else
5343                     pos++;
5344                 }
5345             }
5346         //# #include! yaay
5347         else if (sequ(pos, "#include"))
5348             {
5349             pos += 8;
5350             pos = skipwhite(pos);
5351             String iname;
5352             pos = getword(pos, iname);
5353             if (iname.size()>2)
5354                 {
5355                 iname = iname.substr(1, iname.size()-2);
5356                 addIncludeFile(frec, iname);
5357                 }
5358             }
5359         else
5360             {
5361             pos++;
5362             }
5363         }
5365     return true;
5370 /**
5371  *  Recursively check include lists to find all files in allFiles to which
5372  *  a given file is dependent.
5373  */
5374 bool DepTool::processDependency(FileRec *ofile,
5375                              FileRec *include,
5376                              int depth)
5378     std::map<String, FileRec *>::iterator iter;
5379     for (iter=include->files.begin() ; iter!=include->files.end() ; iter++)
5380         {
5381         String fname  = iter->first;
5382         if (ofile->files.find(fname) != ofile->files.end())
5383             {
5384             //trace("file '%s' already seen", fname.c_str());
5385             continue;
5386             }
5387         FileRec *child  = iter->second;
5388         ofile->files[fname] = child;
5389       
5390         processDependency(ofile, child, depth+1);
5391         }
5394     return true;
5401 /**
5402  *  Generate the file dependency list.
5403  */
5404 bool DepTool::generateDependencies()
5406     std::map<String, FileRec *>::iterator iter;
5407     //# First pass.  Scan for all includes
5408     for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5409         {
5410         FileRec *frec = iter->second;
5411         if (!scanFile(iter->first, frec))
5412             {
5413             //quit?
5414             }
5415         }
5417     //# Second pass.  Scan for all includes
5418     for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5419         {
5420         FileRec *include = iter->second;
5421         if (include->type == FileRec::CFILE)
5422             {
5423             String cFileName = iter->first;
5424             FileRec *ofile      = new FileRec(FileRec::OFILE);
5425             ofile->path         = include->path;
5426             ofile->baseName     = include->baseName;
5427             ofile->suffix       = include->suffix;
5428             String fname     = include->path;
5429             if (fname.size()>0)
5430                 fname.append("/");
5431             fname.append(include->baseName);
5432             fname.append(".o");
5433             depFiles[fname]    = ofile;
5434             //add the .c file first?   no, don't
5435             //ofile->files[cFileName] = include;
5436             
5437             //trace("ofile:%s", fname.c_str());
5439             processDependency(ofile, include, 0);
5440             }
5441         }
5443       
5444     return true;
5449 /**
5450  *  High-level call to generate deps and optionally save them
5451  */
5452 bool DepTool::generateDependencies(const String &fileName)
5454     if (!createFileList())
5455         return false;
5456     if (!generateDependencies())
5457         return false;
5458     if (!saveDepFile(fileName))
5459         return false;
5460     return true;
5464 /**
5465  *   This saves the dependency cache.
5466  */
5467 bool DepTool::saveDepFile(const String &fileName)
5469     time_t tim;
5470     time(&tim);
5472     FILE *f = fopen(fileName.c_str(), "w");
5473     if (!f)
5474         {
5475         trace("cannot open '%s' for writing", fileName.c_str());
5476         }
5477     fprintf(f, "<?xml version='1.0'?>\n");
5478     fprintf(f, "<!--\n");
5479     fprintf(f, "########################################################\n");
5480     fprintf(f, "## File: build.dep\n");
5481     fprintf(f, "## Generated by BuildTool at :%s", ctime(&tim));
5482     fprintf(f, "########################################################\n");
5483     fprintf(f, "-->\n");
5485     fprintf(f, "<dependencies source='%s'>\n\n", sourceDir.c_str());
5486     std::map<String, FileRec *>::iterator iter;
5487     for (iter=depFiles.begin() ; iter!=depFiles.end() ; iter++)
5488         {
5489         FileRec *frec = iter->second;
5490         if (frec->type == FileRec::OFILE)
5491             {
5492             fprintf(f, "<object path='%s' name='%s' suffix='%s'>\n",
5493                  frec->path.c_str(), frec->baseName.c_str(), frec->suffix.c_str());
5494             std::map<String, FileRec *>::iterator citer;
5495             for (citer=frec->files.begin() ; citer!=frec->files.end() ; citer++)
5496                 {
5497                 String cfname = citer->first;
5498                 fprintf(f, "    <dep name='%s'/>\n", cfname.c_str());
5499                 }
5500             fprintf(f, "</object>\n\n");
5501             }
5502         }
5504     fprintf(f, "</dependencies>\n");
5505     fprintf(f, "\n");
5506     fprintf(f, "<!--\n");
5507     fprintf(f, "########################################################\n");
5508     fprintf(f, "## E N D\n");
5509     fprintf(f, "########################################################\n");
5510     fprintf(f, "-->\n");
5512     fclose(f);
5514     return true;
5520 /**
5521  *   This loads the dependency cache.
5522  */
5523 std::vector<DepRec> DepTool::loadDepFile(const String &depFile)
5525     std::vector<DepRec> result;
5526     
5527     Parser parser;
5528     Element *root = parser.parseFile(depFile.c_str());
5529     if (!root)
5530         {
5531         //error("Could not open %s for reading", depFile.c_str());
5532         return result;
5533         }
5535     if (root->getChildren().size()==0 ||
5536         root->getChildren()[0]->getName()!="dependencies")
5537         {
5538         error("Main xml element should be <dependencies>");
5539         delete root;
5540         return result;
5541         }
5543     //########## Start parsing
5544     Element *depList = root->getChildren()[0];
5546     std::vector<Element *> objects = depList->getChildren();
5547     for (unsigned int i=0 ; i<objects.size() ; i++)
5548         {
5549         Element *objectElem = objects[i];
5550         String tagName = objectElem->getName();
5551         if (tagName == "object")
5552             {
5553             String objName   = objectElem->getAttribute("name");
5554              //trace("object:%s", objName.c_str());
5555             DepRec depObject(objName);
5556             depObject.path   = objectElem->getAttribute("path");
5557             depObject.suffix = objectElem->getAttribute("suffix");
5558             //########## DESCRIPTION
5559             std::vector<Element *> depElems = objectElem->getChildren();
5560             for (unsigned int i=0 ; i<depElems.size() ; i++)
5561                 {
5562                 Element *depElem = depElems[i];
5563                 tagName = depElem->getName();
5564                 if (tagName == "dep")
5565                     {
5566                     String depName = depElem->getAttribute("name");
5567                     //trace("    dep:%s", depName.c_str());
5568                     depObject.files.push_back(depName);
5569                     }
5570                 }
5571             //Insert into the result list, in a sorted manner
5572             bool inserted = false;
5573             std::vector<DepRec>::iterator iter;
5574             for (iter = result.begin() ; iter != result.end() ; iter++)
5575                 {
5576                 String vpath = iter->path;
5577                 vpath.append("/");
5578                 vpath.append(iter->name);
5579                 String opath = depObject.path;
5580                 opath.append("/");
5581                 opath.append(depObject.name);
5582                 if (vpath > opath)
5583                     {
5584                     inserted = true;
5585                     iter = result.insert(iter, depObject);
5586                     break;
5587                     }
5588                 }
5589             if (!inserted)
5590                 result.push_back(depObject);
5591             }
5592         }
5594     delete root;
5596     return result;
5600 /**
5601  *   This loads the dependency cache.
5602  */
5603 std::vector<DepRec> DepTool::getDepFile(const String &depFile,
5604                    bool forceRefresh)
5606     std::vector<DepRec> result;
5607     if (forceRefresh)
5608         {
5609         generateDependencies(depFile);
5610         result = loadDepFile(depFile);
5611         }
5612     else
5613         {
5614         //try once
5615         result = loadDepFile(depFile);
5616         if (result.size() == 0)
5617             {
5618             //fail? try again
5619             generateDependencies(depFile);
5620             result = loadDepFile(depFile);
5621             }
5622         }
5623     return result;
5629 //########################################################################
5630 //# T A S K
5631 //########################################################################
5632 //forward decl
5633 class Target;
5634 class Make;
5636 /**
5637  *
5638  */
5639 class Task : public MakeBase
5642 public:
5644     typedef enum
5645         {
5646         TASK_NONE,
5647         TASK_CC,
5648         TASK_COPY,
5649         TASK_DELETE,
5650         TASK_JAR,
5651         TASK_JAVAC,
5652         TASK_LINK,
5653         TASK_MAKEFILE,
5654         TASK_MKDIR,
5655         TASK_MSGFMT,
5656         TASK_RANLIB,
5657         TASK_RC,
5658         TASK_SHAREDLIB,
5659         TASK_STATICLIB,
5660         TASK_STRIP,
5661         TASK_TOUCH,
5662         TASK_TSTAMP
5663         } TaskType;
5664         
5666     /**
5667      *
5668      */
5669     Task(MakeBase &par) : parent(par)
5670         { init(); }
5672     /**
5673      *
5674      */
5675     Task(const Task &other) : parent(other.parent)
5676         { init(); assign(other); }
5678     /**
5679      *
5680      */
5681     Task &operator=(const Task &other)
5682         { assign(other); return *this; }
5684     /**
5685      *
5686      */
5687     virtual ~Task()
5688         { }
5691     /**
5692      *
5693      */
5694     virtual MakeBase &getParent()
5695         { return parent; }
5697      /**
5698      *
5699      */
5700     virtual int  getType()
5701         { return type; }
5703     /**
5704      *
5705      */
5706     virtual void setType(int val)
5707         { type = val; }
5709     /**
5710      *
5711      */
5712     virtual String getName()
5713         { return name; }
5715     /**
5716      *
5717      */
5718     virtual bool execute()
5719         { return true; }
5721     /**
5722      *
5723      */
5724     virtual bool parse(Element *elem)
5725         { return true; }
5727     /**
5728      *
5729      */
5730     Task *createTask(Element *elem);
5733 protected:
5735     void init()
5736         {
5737         type = TASK_NONE;
5738         name = "none";
5739         }
5741     void assign(const Task &other)
5742         {
5743         type = other.type;
5744         name = other.name;
5745         }
5746         
5747     String getAttribute(Element *elem, const String &attrName)
5748         {
5749         String str;
5750         return str;
5751         }
5753     MakeBase &parent;
5755     int type;
5757     String name;
5758 };
5762 /**
5763  * This task runs the C/C++ compiler.  The compiler is invoked
5764  * for all .c or .cpp files which are newer than their correcsponding
5765  * .o files.  
5766  */
5767 class TaskCC : public Task
5769 public:
5771     TaskCC(MakeBase &par) : Task(par)
5772         {
5773         type = TASK_CC; name = "cc";
5774         ccCommand   = "gcc";
5775         cxxCommand  = "g++";
5776         source      = ".";
5777         dest        = ".";
5778         flags       = "";
5779         defines     = "";
5780         includes    = "";
5781         fileSet.clear();
5782         }
5784     virtual ~TaskCC()
5785         {}
5787     virtual bool needsCompiling(const DepRec &depRec,
5788               const String &src, const String &dest)
5789         {
5790         return false;
5791         }
5793     virtual bool execute()
5794         {
5795         if (!listFiles(parent, fileSet))
5796             return false;
5797             
5798         FILE *f = NULL;
5799         f = fopen("compile.lst", "w");
5801         bool refreshCache = false;
5802         String fullName = parent.resolve("build.dep");
5803         if (isNewerThan(parent.getURI().getPath(), fullName))
5804             {
5805             status("          : regenerating C/C++ dependency cache");
5806             refreshCache = true;
5807             }
5809         DepTool depTool;
5810         depTool.setSourceDirectory(source);
5811         depTool.setFileList(fileSet.getFiles());
5812         std::vector<DepRec> deps =
5813              depTool.getDepFile("build.dep", refreshCache);
5814         
5815         String incs;
5816         incs.append("-I");
5817         incs.append(parent.resolve("."));
5818         incs.append(" ");
5819         if (includes.size()>0)
5820             {
5821             incs.append(includes);
5822             incs.append(" ");
5823             }
5824         std::set<String> paths;
5825         std::vector<DepRec>::iterator viter;
5826         for (viter=deps.begin() ; viter!=deps.end() ; viter++)
5827             {
5828             DepRec dep = *viter;
5829             if (dep.path.size()>0)
5830                 paths.insert(dep.path);
5831             }
5832         if (source.size()>0)
5833             {
5834             incs.append(" -I");
5835             incs.append(parent.resolve(source));
5836             incs.append(" ");
5837             }
5838         std::set<String>::iterator setIter;
5839         for (setIter=paths.begin() ; setIter!=paths.end() ; setIter++)
5840             {
5841             incs.append(" -I");
5842             String dname;
5843             if (source.size()>0)
5844                 {
5845                 dname.append(source);
5846                 dname.append("/");
5847                 }
5848             dname.append(*setIter);
5849             incs.append(parent.resolve(dname));
5850             }
5851         std::vector<String> cfiles;
5852         for (viter=deps.begin() ; viter!=deps.end() ; viter++)
5853             {
5854             DepRec dep = *viter;
5856             //## Select command
5857             String sfx = dep.suffix;
5858             String command = ccCommand;
5859             if (sfx == "cpp" || sfx == "c++" || sfx == "cc"
5860                  || sfx == "CC")
5861                 command = cxxCommand;
5862  
5863             //## Make paths
5864             String destPath = dest;
5865             String srcPath  = source;
5866             if (dep.path.size()>0)
5867                 {
5868                 destPath.append("/");
5869                 destPath.append(dep.path);
5870                 srcPath.append("/");
5871                 srcPath.append(dep.path);
5872                 }
5873             //## Make sure destination directory exists
5874             if (!createDirectory(destPath))
5875                 return false;
5876                 
5877             //## Check whether it needs to be done
5878             String destName;
5879             if (destPath.size()>0)
5880                 {
5881                 destName.append(destPath);
5882                 destName.append("/");
5883                 }
5884             destName.append(dep.name);
5885             destName.append(".o");
5886             String destFullName = parent.resolve(destName);
5887             String srcName;
5888             if (srcPath.size()>0)
5889                 {
5890                 srcName.append(srcPath);
5891                 srcName.append("/");
5892                 }
5893             srcName.append(dep.name);
5894             srcName.append(".");
5895             srcName.append(dep.suffix);
5896             String srcFullName = parent.resolve(srcName);
5897             bool compileMe = false;
5898             if (isNewerThan(srcFullName, destFullName))
5899                 {
5900                 status("          : compile of %s required by %s",
5901                         destFullName.c_str(), srcFullName.c_str());
5902                 compileMe = true;
5903                 }
5904             else
5905                 {
5906                 for (unsigned int i=0 ; i<dep.files.size() ; i++)
5907                     {
5908                     String depName;
5909                     if (srcPath.size()>0)
5910                         {
5911                         depName.append(srcPath);
5912                         depName.append("/");
5913                         }
5914                     depName.append(dep.files[i]);
5915                     String depFullName = parent.resolve(depName);
5916                     if (isNewerThan(depFullName, destFullName))
5917                         {
5918                         status("          : compile of %s required by %s",
5919                                 destFullName.c_str(), depFullName.c_str());
5920                         compileMe = true;
5921                         break;
5922                         }
5923                     }
5924                 }
5925             if (!compileMe)
5926                 {
5927                 continue;
5928                 }
5930             //## Assemble the command
5931             String cmd = command;
5932             cmd.append(" -c ");
5933             cmd.append(flags);
5934             cmd.append(" ");
5935             cmd.append(defines);
5936             cmd.append(" ");
5937             cmd.append(incs);
5938             cmd.append(" ");
5939             cmd.append(srcFullName);
5940             cmd.append(" -o ");
5941             cmd.append(destFullName);
5943             //## Execute the command
5945             String outString, errString;
5946             bool ret = executeCommand(cmd.c_str(), "", outString, errString);
5947             if (f)
5948                 {
5949                 fprintf(f, "########################### File : %s\n",
5950                              srcFullName.c_str());
5951                 fprintf(f, "#### COMMAND ###\n");
5952                 int col = 0;
5953                 for (int i = 0 ; i < cmd.size() ; i++)
5954                     {
5955                     char ch = cmd[i];
5956                     if (isspace(ch)  && col > 63)
5957                         {
5958                         fputc('\n', f);
5959                         col = 0;
5960                         }
5961                     else
5962                         {
5963                         fputc(ch, f);
5964                         col++;
5965                         }
5966                     if (col > 76)
5967                         {
5968                         fputc('\n', f);
5969                         col = 0;
5970                         }
5971                     }
5972                 fprintf(f, "\n");
5973                 fprintf(f, "#### STDOUT ###\n%s\n", outString.c_str());
5974                 fprintf(f, "#### STDERR ###\n%s\n\n", errString.c_str());
5975                 }
5976             if (!ret)
5977                 {
5978                 error("problem compiling: %s", errString.c_str());
5979                 return false;
5980                 }
5981                 
5982             }
5984         if (f)
5985             {
5986             fclose(f);
5987             }
5988         
5989         return true;
5990         }
5992     virtual bool parse(Element *elem)
5993         {
5994         String s;
5995         if (!parent.getAttribute(elem, "command", s))
5996             return false;
5997         if (s.size()>0) { ccCommand = s; cxxCommand = s; }
5998         if (!parent.getAttribute(elem, "cc", s))
5999             return false;
6000         if (s.size()>0) ccCommand = s;
6001         if (!parent.getAttribute(elem, "cxx", s))
6002             return false;
6003         if (s.size()>0) cxxCommand = s;
6004         if (!parent.getAttribute(elem, "destdir", s))
6005             return false;
6006         if (s.size()>0) dest = s;
6008         std::vector<Element *> children = elem->getChildren();
6009         for (unsigned int i=0 ; i<children.size() ; i++)
6010             {
6011             Element *child = children[i];
6012             String tagName = child->getName();
6013             if (tagName == "flags")
6014                 {
6015                 if (!parent.getValue(child, flags))
6016                     return false;
6017                 flags = strip(flags);
6018                 }
6019             else if (tagName == "includes")
6020                 {
6021                 if (!parent.getValue(child, includes))
6022                     return false;
6023                 includes = strip(includes);
6024                 }
6025             else if (tagName == "defines")
6026                 {
6027                 if (!parent.getValue(child, defines))
6028                     return false;
6029                 defines = strip(defines);
6030                 }
6031             else if (tagName == "fileset")
6032                 {
6033                 if (!parseFileSet(child, parent, fileSet))
6034                     return false;
6035                 source = fileSet.getDirectory();
6036                 }
6037             }
6039         return true;
6040         }
6041         
6042 protected:
6044     String ccCommand;
6045     String cxxCommand;
6046     String source;
6047     String dest;
6048     String flags;
6049     String defines;
6050     String includes;
6051     FileSet fileSet;
6052     
6053 };
6057 /**
6058  *
6059  */
6060 class TaskCopy : public Task
6062 public:
6064     typedef enum
6065         {
6066         CP_NONE,
6067         CP_TOFILE,
6068         CP_TODIR
6069         } CopyType;
6071     TaskCopy(MakeBase &par) : Task(par)
6072         {
6073         type = TASK_COPY; name = "copy";
6074         cptype = CP_NONE;
6075         verbose = false;
6076         haveFileSet = false;
6077         }
6079     virtual ~TaskCopy()
6080         {}
6082     virtual bool execute()
6083         {
6084         switch (cptype)
6085            {
6086            case CP_TOFILE:
6087                {
6088                if (fileName.size()>0)
6089                    {
6090                    status("          : %s to %s",
6091                         fileName.c_str(), toFileName.c_str());
6092                    String fullSource = parent.resolve(fileName);
6093                    String fullDest = parent.resolve(toFileName);
6094                    //trace("copy %s to file %s", fullSource.c_str(),
6095                    //                       fullDest.c_str());
6096                    if (!isRegularFile(fullSource))
6097                        {
6098                        error("copy : file %s does not exist", fullSource.c_str());
6099                        return false;
6100                        }
6101                    if (!isNewerThan(fullSource, fullDest))
6102                        {
6103                        return true;
6104                        }
6105                    if (!copyFile(fullSource, fullDest))
6106                        return false;
6107                    status("          : 1 file copied");
6108                    }
6109                return true;
6110                }
6111            case CP_TODIR:
6112                {
6113                if (haveFileSet)
6114                    {
6115                    if (!listFiles(parent, fileSet))
6116                        return false;
6117                    String fileSetDir = fileSet.getDirectory();
6119                    status("          : %s to %s",
6120                        fileSetDir.c_str(), toDirName.c_str());
6122                    int nrFiles = 0;
6123                    for (unsigned int i=0 ; i<fileSet.size() ; i++)
6124                        {
6125                        String fileName = fileSet[i];
6127                        String sourcePath;
6128                        if (fileSetDir.size()>0)
6129                            {
6130                            sourcePath.append(fileSetDir);
6131                            sourcePath.append("/");
6132                            }
6133                        sourcePath.append(fileName);
6134                        String fullSource = parent.resolve(sourcePath);
6135                        
6136                        //Get the immediate parent directory's base name
6137                        String baseFileSetDir = fileSetDir;
6138                        unsigned int pos = baseFileSetDir.find_last_of('/');
6139                        if (pos!=baseFileSetDir.npos &&
6140                                   pos < baseFileSetDir.size()-1)
6141                            baseFileSetDir =
6142                               baseFileSetDir.substr(pos+1,
6143                                    baseFileSetDir.size());
6144                        //Now make the new path
6145                        String destPath;
6146                        if (toDirName.size()>0)
6147                            {
6148                            destPath.append(toDirName);
6149                            destPath.append("/");
6150                            }
6151                        if (baseFileSetDir.size()>0)
6152                            {
6153                            destPath.append(baseFileSetDir);
6154                            destPath.append("/");
6155                            }
6156                        destPath.append(fileName);
6157                        String fullDest = parent.resolve(destPath);
6158                        //trace("fileName:%s", fileName.c_str());
6159                        //trace("copy %s to new dir : %s", fullSource.c_str(),
6160                        //                   fullDest.c_str());
6161                        if (!isNewerThan(fullSource, fullDest))
6162                            {
6163                            //trace("copy skipping %s", fullSource.c_str());
6164                            continue;
6165                            }
6166                        if (!copyFile(fullSource, fullDest))
6167                            return false;
6168                        nrFiles++;
6169                        }
6170                    status("          : %d file(s) copied", nrFiles);
6171                    }
6172                else //file source
6173                    {
6174                    //For file->dir we want only the basename of
6175                    //the source appended to the dest dir
6176                    status("          : %s to %s", 
6177                        fileName.c_str(), toDirName.c_str());
6178                    String baseName = fileName;
6179                    unsigned int pos = baseName.find_last_of('/');
6180                    if (pos!=baseName.npos && pos<baseName.size()-1)
6181                        baseName = baseName.substr(pos+1, baseName.size());
6182                    String fullSource = parent.resolve(fileName);
6183                    String destPath;
6184                    if (toDirName.size()>0)
6185                        {
6186                        destPath.append(toDirName);
6187                        destPath.append("/");
6188                        }
6189                    destPath.append(baseName);
6190                    String fullDest = parent.resolve(destPath);
6191                    //trace("copy %s to new dir : %s", fullSource.c_str(),
6192                    //                       fullDest.c_str());
6193                    if (!isRegularFile(fullSource))
6194                        {
6195                        error("copy : file %s does not exist", fullSource.c_str());
6196                        return false;
6197                        }
6198                    if (!isNewerThan(fullSource, fullDest))
6199                        {
6200                        return true;
6201                        }
6202                    if (!copyFile(fullSource, fullDest))
6203                        return false;
6204                    status("          : 1 file copied");
6205                    }
6206                return true;
6207                }
6208            }
6209         return true;
6210         }
6213     virtual bool parse(Element *elem)
6214         {
6215         if (!parent.getAttribute(elem, "file", fileName))
6216             return false;
6217         if (!parent.getAttribute(elem, "tofile", toFileName))
6218             return false;
6219         if (toFileName.size() > 0)
6220             cptype = CP_TOFILE;
6221         if (!parent.getAttribute(elem, "todir", toDirName))
6222             return false;
6223         if (toDirName.size() > 0)
6224             cptype = CP_TODIR;
6225         String ret;
6226         if (!parent.getAttribute(elem, "verbose", ret))
6227             return false;
6228         if (ret.size()>0 && !getBool(ret, verbose))
6229             return false;
6230             
6231         haveFileSet = false;
6232         
6233         std::vector<Element *> children = elem->getChildren();
6234         for (unsigned int i=0 ; i<children.size() ; i++)
6235             {
6236             Element *child = children[i];
6237             String tagName = child->getName();
6238             if (tagName == "fileset")
6239                 {
6240                 if (!parseFileSet(child, parent, fileSet))
6241                     {
6242                     error("problem getting fileset");
6243                     return false;
6244                     }
6245                 haveFileSet = true;
6246                 }
6247             }
6249         //Perform validity checks
6250         if (fileName.size()>0 && fileSet.size()>0)
6251             {
6252             error("<copy> can only have one of : file= and <fileset>");
6253             return false;
6254             }
6255         if (toFileName.size()>0 && toDirName.size()>0)
6256             {
6257             error("<copy> can only have one of : tofile= or todir=");
6258             return false;
6259             }
6260         if (haveFileSet && toDirName.size()==0)
6261             {
6262             error("a <copy> task with a <fileset> must have : todir=");
6263             return false;
6264             }
6265         if (cptype == CP_TOFILE && fileName.size()==0)
6266             {
6267             error("<copy> tofile= must be associated with : file=");
6268             return false;
6269             }
6270         if (cptype == CP_TODIR && fileName.size()==0 && !haveFileSet)
6271             {
6272             error("<copy> todir= must be associated with : file= or <fileset>");
6273             return false;
6274             }
6276         return true;
6277         }
6278         
6279 private:
6281     int cptype;
6282     String fileName;
6283     FileSet fileSet;
6284     String toFileName;
6285     String toDirName;
6286     bool verbose;
6287     bool haveFileSet;
6288 };
6291 /**
6292  *
6293  */
6294 class TaskDelete : public Task
6296 public:
6298     typedef enum
6299         {
6300         DEL_FILE,
6301         DEL_DIR,
6302         DEL_FILESET
6303         } DeleteType;
6305     TaskDelete(MakeBase &par) : Task(par)
6306         { 
6307           type        = TASK_DELETE;
6308           name        = "delete";
6309           delType     = DEL_FILE;
6310           verbose     = false;
6311           quiet       = false;
6312           failOnError = true;
6313         }
6315     virtual ~TaskDelete()
6316         {}
6318     virtual bool execute()
6319         {
6320         struct stat finfo;
6321         switch (delType)
6322             {
6323             case DEL_FILE:
6324                 {
6325                 status("          : %s", fileName.c_str());
6326                 String fullName = parent.resolve(fileName);
6327                 char *fname = (char *)fullName.c_str();
6328                 //does not exist
6329                 if (stat(fname, &finfo)<0)
6330                     return true;
6331                 //exists but is not a regular file
6332                 if (!S_ISREG(finfo.st_mode))
6333                     {
6334                     error("<delete> failed. '%s' exists and is not a regular file",
6335                           fname);
6336                     return false;
6337                     }
6338                 if (remove(fname)<0)
6339                     {
6340                     error("<delete> failed: %s", strerror(errno));
6341                     return false;
6342                     }
6343                 return true;
6344                 }
6345             case DEL_DIR:
6346                 {
6347                 status("          : %s", dirName.c_str());
6348                 String fullDir = parent.resolve(dirName);
6349                 if (!removeDirectory(fullDir))
6350                     return false;
6351                 return true;
6352                 }
6353             }
6354         return true;
6355         }
6357     virtual bool parse(Element *elem)
6358         {
6359         if (!parent.getAttribute(elem, "file", fileName))
6360             return false;
6361         if (fileName.size() > 0)
6362             delType = DEL_FILE;
6363         if (!parent.getAttribute(elem, "dir", dirName))
6364             return false;
6365         if (dirName.size() > 0)
6366             delType = DEL_DIR;
6367         if (fileName.size()>0 && dirName.size()>0)
6368             {
6369             error("<delete> can only have one attribute of file= or dir=");
6370             return false;
6371             }
6372         String ret;
6373         if (!parent.getAttribute(elem, "verbose", ret))
6374             return false;
6375         if (ret.size()>0 && !getBool(ret, verbose))
6376             return false;
6377         if (!parent.getAttribute(elem, "quiet", ret))
6378             return false;
6379         if (ret.size()>0 && !getBool(ret, quiet))
6380             return false;
6381         if (!parent.getAttribute(elem, "failonerror", ret))
6382             return false;
6383         if (ret.size()>0 && !getBool(ret, failOnError))
6384             return false;
6385         return true;
6386         }
6388 private:
6390     int delType;
6391     String dirName;
6392     String fileName;
6393     bool verbose;
6394     bool quiet;
6395     bool failOnError;
6396 };
6399 /**
6400  *
6401  */
6402 class TaskJar : public Task
6404 public:
6406     TaskJar(MakeBase &par) : Task(par)
6407         { type = TASK_JAR; name = "jar"; }
6409     virtual ~TaskJar()
6410         {}
6412     virtual bool execute()
6413         {
6414         return true;
6415         }
6417     virtual bool parse(Element *elem)
6418         {
6419         return true;
6420         }
6421 };
6424 /**
6425  *
6426  */
6427 class TaskJavac : public Task
6429 public:
6431     TaskJavac(MakeBase &par) : Task(par)
6432         { type = TASK_JAVAC; name = "javac"; }
6434     virtual ~TaskJavac()
6435         {}
6437     virtual bool execute()
6438         {
6439         return true;
6440         }
6442     virtual bool parse(Element *elem)
6443         {
6444         return true;
6445         }
6446 };
6449 /**
6450  *
6451  */
6452 class TaskLink : public Task
6454 public:
6456     TaskLink(MakeBase &par) : Task(par)
6457         {
6458         type = TASK_LINK; name = "link";
6459         command = "g++";
6460         doStrip = false;
6461                 stripCommand = "strip";
6462                 objcopyCommand = "objcopy";
6463         }
6465     virtual ~TaskLink()
6466         {}
6468     virtual bool execute()
6469         {
6470         if (!listFiles(parent, fileSet))
6471             return false;
6472         String fileSetDir = fileSet.getDirectory();
6473         //trace("%d files in %s", fileSet.size(), fileSetDir.c_str());
6474         bool doit = false;
6475         String fullTarget = parent.resolve(fileName);
6476         String cmd = command;
6477         cmd.append(" -o ");
6478         cmd.append(fullTarget);
6479         cmd.append(" ");
6480         cmd.append(flags);
6481         for (unsigned int i=0 ; i<fileSet.size() ; i++)
6482             {
6483             cmd.append(" ");
6484             String obj;
6485             if (fileSetDir.size()>0)
6486                 {
6487                 obj.append(fileSetDir);
6488                 obj.append("/");
6489                 }
6490             obj.append(fileSet[i]);
6491             String fullObj = parent.resolve(obj);
6492             cmd.append(fullObj);
6493             //trace("link: tgt:%s obj:%s", fullTarget.c_str(),
6494             //          fullObj.c_str());
6495             if (isNewerThan(fullObj, fullTarget))
6496                 doit = true;
6497             }
6498         cmd.append(" ");
6499         cmd.append(libs);
6500         if (!doit)
6501             {
6502             //trace("link not needed");
6503             return true;
6504             }
6505         //trace("LINK cmd:%s", cmd.c_str());
6508         String outbuf, errbuf;
6509         if (!executeCommand(cmd.c_str(), "", outbuf, errbuf))
6510             {
6511             error("LINK problem: %s", errbuf.c_str());
6512             return false;
6513             }
6515         if (symFileName.size()>0)
6516             {
6517             String symFullName = parent.resolve(symFileName);
6518             cmd = objcopyCommand;
6519             cmd.append(" --only-keep-debug ");
6520             cmd.append(getNativePath(fullTarget));
6521             cmd.append(" ");
6522             cmd.append(getNativePath(symFullName));
6523             if (!executeCommand(cmd, "", outbuf, errbuf))
6524                 {
6525                 error("<strip> symbol file failed : %s", errbuf.c_str());
6526                 return false;
6527                 }
6528             }
6529             
6530         if (doStrip)
6531             {
6532             cmd = stripCommand;
6533             cmd.append(" ");
6534             cmd.append(getNativePath(fullTarget));
6535             if (!executeCommand(cmd, "", outbuf, errbuf))
6536                {
6537                error("<strip> failed : %s", errbuf.c_str());
6538                return false;
6539                }
6540             }
6542         return true;
6543         }
6545     virtual bool parse(Element *elem)
6546         {
6547         String s;
6548         if (!parent.getAttribute(elem, "command", s))
6549             return false;
6550         if (s.size()>0)
6551             command = s;
6552         if (!parent.getAttribute(elem, "objcopycommand", s))
6553             return false;
6554         if (s.size()>0)
6555             objcopyCommand = s;
6556         if (!parent.getAttribute(elem, "stripcommand", s))
6557             return false;
6558         if (s.size()>0)
6559             stripCommand = s;
6560         if (!parent.getAttribute(elem, "out", fileName))
6561             return false;
6562         if (!parent.getAttribute(elem, "strip", s))
6563             return false;
6564         if (s.size()>0 && !getBool(s, doStrip))
6565             return false;
6566         if (!parent.getAttribute(elem, "symfile", symFileName))
6567             return false;
6568             
6569         std::vector<Element *> children = elem->getChildren();
6570         for (unsigned int i=0 ; i<children.size() ; i++)
6571             {
6572             Element *child = children[i];
6573             String tagName = child->getName();
6574             if (tagName == "fileset")
6575                 {
6576                 if (!parseFileSet(child, parent, fileSet))
6577                     return false;
6578                 }
6579             else if (tagName == "flags")
6580                 {
6581                 if (!parent.getValue(child, flags))
6582                     return false;
6583                 flags = strip(flags);
6584                 }
6585             else if (tagName == "libs")
6586                 {
6587                 if (!parent.getValue(child, libs))
6588                     return false;
6589                 libs = strip(libs);
6590                 }
6591             }
6592         return true;
6593         }
6595 private:
6597     String  command;
6598     String  fileName;
6599     String  flags;
6600     String  libs;
6601     FileSet fileSet;
6602     bool    doStrip;
6603     String  symFileName;
6604     String  stripCommand;
6605     String  objcopyCommand;
6607 };
6611 /**
6612  * Create a named directory
6613  */
6614 class TaskMakeFile : public Task
6616 public:
6618     TaskMakeFile(MakeBase &par) : Task(par)
6619         { type = TASK_MAKEFILE; name = "makefile"; }
6621     virtual ~TaskMakeFile()
6622         {}
6624     virtual bool execute()
6625         {
6626         status("          : %s", fileName.c_str());
6627         String fullName = parent.resolve(fileName);
6628         if (!isNewerThan(parent.getURI().getPath(), fullName))
6629             {
6630             //trace("skipped <makefile>");
6631             return true;
6632             }
6633         //trace("fullName:%s", fullName.c_str());
6634         FILE *f = fopen(fullName.c_str(), "w");
6635         if (!f)
6636             {
6637             error("<makefile> could not open %s for writing : %s",
6638                 fullName.c_str(), strerror(errno));
6639             return false;
6640             }
6641         for (unsigned int i=0 ; i<text.size() ; i++)
6642             fputc(text[i], f);
6643         fputc('\n', f);
6644         fclose(f);
6645         return true;
6646         }
6648     virtual bool parse(Element *elem)
6649         {
6650         if (!parent.getAttribute(elem, "file", fileName))
6651             return false;
6652         if (fileName.size() == 0)
6653             {
6654             error("<makefile> requires 'file=\"filename\"' attribute");
6655             return false;
6656             }
6657         if (!parent.getValue(elem, text))
6658             return false;
6659         text = leftJustify(text);
6660         //trace("dirname:%s", dirName.c_str());
6661         return true;
6662         }
6664 private:
6666     String fileName;
6667     String text;
6668 };
6672 /**
6673  * Create a named directory
6674  */
6675 class TaskMkDir : public Task
6677 public:
6679     TaskMkDir(MakeBase &par) : Task(par)
6680         { type = TASK_MKDIR; name = "mkdir"; }
6682     virtual ~TaskMkDir()
6683         {}
6685     virtual bool execute()
6686         {
6687         status("          : %s", dirName.c_str());
6688         String fullDir = parent.resolve(dirName);
6689         //trace("fullDir:%s", fullDir.c_str());
6690         if (!createDirectory(fullDir))
6691             return false;
6692         return true;
6693         }
6695     virtual bool parse(Element *elem)
6696         {
6697         if (!parent.getAttribute(elem, "dir", dirName))
6698             return false;
6699         if (dirName.size() == 0)
6700             {
6701             error("<mkdir> requires 'dir=\"dirname\"' attribute");
6702             return false;
6703             }
6704         return true;
6705         }
6707 private:
6709     String dirName;
6710 };
6714 /**
6715  * Create a named directory
6716  */
6717 class TaskMsgFmt: public Task
6719 public:
6721     TaskMsgFmt(MakeBase &par) : Task(par)
6722          {
6723          type    = TASK_MSGFMT;
6724          name    = "msgfmt";
6725          command = "msgfmt";
6726          owndir  = false;
6727          outName = "";
6728          }
6730     virtual ~TaskMsgFmt()
6731         {}
6733     virtual bool execute()
6734         {
6735         if (!listFiles(parent, fileSet))
6736             return false;
6737         String fileSetDir = fileSet.getDirectory();
6739         //trace("msgfmt: %d", fileSet.size());
6740         for (unsigned int i=0 ; i<fileSet.size() ; i++)
6741             {
6742             String fileName = fileSet[i];
6743             if (getSuffix(fileName) != "po")
6744                 continue;
6745             String sourcePath;
6746             if (fileSetDir.size()>0)
6747                 {
6748                 sourcePath.append(fileSetDir);
6749                 sourcePath.append("/");
6750                 }
6751             sourcePath.append(fileName);
6752             String fullSource = parent.resolve(sourcePath);
6754             String destPath;
6755             if (toDirName.size()>0)
6756                 {
6757                 destPath.append(toDirName);
6758                 destPath.append("/");
6759                 }
6760             if (owndir)
6761                 {
6762                 String subdir = fileName;
6763                 unsigned int pos = subdir.find_last_of('.');
6764                 if (pos != subdir.npos)
6765                     subdir = subdir.substr(0, pos);
6766                 destPath.append(subdir);
6767                 destPath.append("/");
6768                 }
6769             //Pick the output file name
6770             if (outName.size() > 0)
6771                 {
6772                 destPath.append(outName);
6773                 }
6774             else
6775                 {
6776                 destPath.append(fileName);
6777                 destPath[destPath.size()-2] = 'm';
6778                 }
6780             String fullDest = parent.resolve(destPath);
6782             if (!isNewerThan(fullSource, fullDest))
6783                 {
6784                 //trace("skip %s", fullSource.c_str());
6785                 continue;
6786                 }
6787                 
6788             String cmd = command;
6789             cmd.append(" ");
6790             cmd.append(fullSource);
6791             cmd.append(" -o ");
6792             cmd.append(fullDest);
6793             
6794             int pos = fullDest.find_last_of('/');
6795             if (pos>0)
6796                 {
6797                 String fullDestPath = fullDest.substr(0, pos);
6798                 if (!createDirectory(fullDestPath))
6799                     return false;
6800                 }
6804             String outString, errString;
6805             if (!executeCommand(cmd.c_str(), "", outString, errString))
6806                 {
6807                 error("<msgfmt> problem: %s", errString.c_str());
6808                 return false;
6809                 }
6810             }
6812         return true;
6813         }
6815     virtual bool parse(Element *elem)
6816         {
6817         String s;
6818         if (!parent.getAttribute(elem, "command", s))
6819             return false;
6820         if (s.size()>0)
6821             command = s;
6822         if (!parent.getAttribute(elem, "todir", toDirName))
6823             return false;
6824         if (!parent.getAttribute(elem, "out", outName))
6825             return false;
6826         if (!parent.getAttribute(elem, "owndir", s))
6827             return false;
6828         if (s.size()>0 && !getBool(s, owndir))
6829             return false;
6830             
6831         std::vector<Element *> children = elem->getChildren();
6832         for (unsigned int i=0 ; i<children.size() ; i++)
6833             {
6834             Element *child = children[i];
6835             String tagName = child->getName();
6836             if (tagName == "fileset")
6837                 {
6838                 if (!parseFileSet(child, parent, fileSet))
6839                     return false;
6840                 }
6841             }
6842         return true;
6843         }
6845 private:
6847     String  command;
6848     String  toDirName;
6849     String  outName;
6850     FileSet fileSet;
6851     bool    owndir;
6853 };
6859 /**
6860  *  Process an archive to allow random access
6861  */
6862 class TaskRanlib : public Task
6864 public:
6866     TaskRanlib(MakeBase &par) : Task(par)
6867         {
6868         type = TASK_RANLIB; name = "ranlib";
6869         command = "ranlib";
6870         }
6872     virtual ~TaskRanlib()
6873         {}
6875     virtual bool execute()
6876         {
6877         String fullName = parent.resolve(fileName);
6878         //trace("fullDir:%s", fullDir.c_str());
6879         String cmd = command;
6880         cmd.append(" ");
6881         cmd.append(fullName);
6882         String outbuf, errbuf;
6883         if (!executeCommand(cmd, "", outbuf, errbuf))
6884             return false;
6885         return true;
6886         }
6888     virtual bool parse(Element *elem)
6889         {
6890         String s;
6891         if (!parent.getAttribute(elem, "command", s))
6892             return false;
6893         if (s.size()>0)
6894            command = s;
6895         if (!parent.getAttribute(elem, "file", fileName))
6896             return false;
6897         if (fileName.size() == 0)
6898             {
6899             error("<ranlib> requires 'file=\"fileNname\"' attribute");
6900             return false;
6901             }
6902         return true;
6903         }
6905 private:
6907     String fileName;
6908     String command;
6909 };
6913 /**
6914  * Run the "ar" command to archive .o's into a .a
6915  */
6916 class TaskRC : public Task
6918 public:
6920     TaskRC(MakeBase &par) : Task(par)
6921         {
6922         type = TASK_RC; name = "rc";
6923         command = "windres";
6924         }
6926     virtual ~TaskRC()
6927         {}
6929     virtual bool execute()
6930         {
6931         String fullFile = parent.resolve(fileName);
6932         String fullOut  = parent.resolve(outName);
6933         if (!isNewerThan(fullFile, fullOut))
6934             return true;
6935         String cmd = command;
6936         cmd.append(" -o ");
6937         cmd.append(fullOut);
6938         cmd.append(" ");
6939         cmd.append(flags);
6940         cmd.append(" ");
6941         cmd.append(fullFile);
6943         String outString, errString;
6944         if (!executeCommand(cmd.c_str(), "", outString, errString))
6945             {
6946             error("RC problem: %s", errString.c_str());
6947             return false;
6948             }
6949         return true;
6950         }
6952     virtual bool parse(Element *elem)
6953         {
6954         if (!parent.getAttribute(elem, "command", command))
6955             return false;
6956         if (!parent.getAttribute(elem, "file", fileName))
6957             return false;
6958         if (!parent.getAttribute(elem, "out", outName))
6959             return false;
6960         std::vector<Element *> children = elem->getChildren();
6961         for (unsigned int i=0 ; i<children.size() ; i++)
6962             {
6963             Element *child = children[i];
6964             String tagName = child->getName();
6965             if (tagName == "flags")
6966                 {
6967                 if (!parent.getValue(child, flags))
6968                     return false;
6969                 }
6970             }
6971         return true;
6972         }
6974 private:
6976     String command;
6977     String flags;
6978     String fileName;
6979     String outName;
6981 };
6985 /**
6986  *  Collect .o's into a .so or DLL
6987  */
6988 class TaskSharedLib : public Task
6990 public:
6992     TaskSharedLib(MakeBase &par) : Task(par)
6993         {
6994         type = TASK_SHAREDLIB; name = "dll";
6995         command = "ar crv";
6996         }
6998     virtual ~TaskSharedLib()
6999         {}
7001     virtual bool execute()
7002         {
7003         //trace("###########HERE %d", fileSet.size());
7004         bool doit = false;
7005         
7006         String fullOut = parent.resolve(fileName);
7007         //trace("ar fullout: %s", fullOut.c_str());
7008         
7009         if (!listFiles(parent, fileSet))
7010             return false;
7011         String fileSetDir = fileSet.getDirectory();
7013         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7014             {
7015             String fname;
7016             if (fileSetDir.size()>0)
7017                 {
7018                 fname.append(fileSetDir);
7019                 fname.append("/");
7020                 }
7021             fname.append(fileSet[i]);
7022             String fullName = parent.resolve(fname);
7023             //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
7024             if (isNewerThan(fullName, fullOut))
7025                 doit = true;
7026             }
7027         //trace("Needs it:%d", doit);
7028         if (!doit)
7029             {
7030             return true;
7031             }
7033         String cmd = "dllwrap";
7034         cmd.append(" -o ");
7035         cmd.append(fullOut);
7036         if (defFileName.size()>0)
7037             {
7038             cmd.append(" --def ");
7039             cmd.append(defFileName);
7040             cmd.append(" ");
7041             }
7042         if (impFileName.size()>0)
7043             {
7044             cmd.append(" --implib ");
7045             cmd.append(impFileName);
7046             cmd.append(" ");
7047             }
7048         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7049             {
7050             String fname;
7051             if (fileSetDir.size()>0)
7052                 {
7053                 fname.append(fileSetDir);
7054                 fname.append("/");
7055                 }
7056             fname.append(fileSet[i]);
7057             String fullName = parent.resolve(fname);
7059             cmd.append(" ");
7060             cmd.append(fullName);
7061             }
7062         cmd.append(" ");
7063         cmd.append(libs);
7065         String outString, errString;
7066         if (!executeCommand(cmd.c_str(), "", outString, errString))
7067             {
7068             error("<sharedlib> problem: %s", errString.c_str());
7069             return false;
7070             }
7072         return true;
7073         }
7075     virtual bool parse(Element *elem)
7076         {
7077         if (!parent.getAttribute(elem, "file", fileName))
7078             return false;
7079         if (!parent.getAttribute(elem, "import", impFileName))
7080             return false;
7081         if (!parent.getAttribute(elem, "def", defFileName))
7082             return false;
7083             
7084         std::vector<Element *> children = elem->getChildren();
7085         for (unsigned int i=0 ; i<children.size() ; i++)
7086             {
7087             Element *child = children[i];
7088             String tagName = child->getName();
7089             if (tagName == "fileset")
7090                 {
7091                 if (!parseFileSet(child, parent, fileSet))
7092                     return false;
7093                 }
7094             else if (tagName == "libs")
7095                 {
7096                 if (!parent.getValue(child, libs))
7097                     return false;
7098                 libs = strip(libs);
7099                 }
7100             }
7101         return true;
7102         }
7104 private:
7106     String command;
7107     String fileName;
7108     String defFileName;
7109     String impFileName;
7110     FileSet fileSet;
7111     String libs;
7113 };
7117 /**
7118  * Run the "ar" command to archive .o's into a .a
7119  */
7120 class TaskStaticLib : public Task
7122 public:
7124     TaskStaticLib(MakeBase &par) : Task(par)
7125         {
7126         type = TASK_STATICLIB; name = "staticlib";
7127         command = "ar crv";
7128         }
7130     virtual ~TaskStaticLib()
7131         {}
7133     virtual bool execute()
7134         {
7135         //trace("###########HERE %d", fileSet.size());
7136         bool doit = false;
7137         
7138         String fullOut = parent.resolve(fileName);
7139         //trace("ar fullout: %s", fullOut.c_str());
7140         
7141         if (!listFiles(parent, fileSet))
7142             return false;
7143         String fileSetDir = fileSet.getDirectory();
7145         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7146             {
7147             String fname;
7148             if (fileSetDir.size()>0)
7149                 {
7150                 fname.append(fileSetDir);
7151                 fname.append("/");
7152                 }
7153             fname.append(fileSet[i]);
7154             String fullName = parent.resolve(fname);
7155             //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
7156             if (isNewerThan(fullName, fullOut))
7157                 doit = true;
7158             }
7159         //trace("Needs it:%d", doit);
7160         if (!doit)
7161             {
7162             return true;
7163             }
7165         String cmd = command;
7166         cmd.append(" ");
7167         cmd.append(fullOut);
7168         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7169             {
7170             String fname;
7171             if (fileSetDir.size()>0)
7172                 {
7173                 fname.append(fileSetDir);
7174                 fname.append("/");
7175                 }
7176             fname.append(fileSet[i]);
7177             String fullName = parent.resolve(fname);
7179             cmd.append(" ");
7180             cmd.append(fullName);
7181             }
7183         String outString, errString;
7184         if (!executeCommand(cmd.c_str(), "", outString, errString))
7185             {
7186             error("<staticlib> problem: %s", errString.c_str());
7187             return false;
7188             }
7190         return true;
7191         }
7194     virtual bool parse(Element *elem)
7195         {
7196         String s;
7197         if (!parent.getAttribute(elem, "command", s))
7198             return false;
7199         if (s.size()>0)
7200             command = s;
7201         if (!parent.getAttribute(elem, "file", fileName))
7202             return false;
7203             
7204         std::vector<Element *> children = elem->getChildren();
7205         for (unsigned int i=0 ; i<children.size() ; i++)
7206             {
7207             Element *child = children[i];
7208             String tagName = child->getName();
7209             if (tagName == "fileset")
7210                 {
7211                 if (!parseFileSet(child, parent, fileSet))
7212                     return false;
7213                 }
7214             }
7215         return true;
7216         }
7218 private:
7220     String command;
7221     String fileName;
7222     FileSet fileSet;
7224 };
7229 /**
7230  * Strip an executable
7231  */
7232 class TaskStrip : public Task
7234 public:
7236     TaskStrip(MakeBase &par) : Task(par)
7237         { type = TASK_STRIP; name = "strip"; }
7239     virtual ~TaskStrip()
7240         {}
7242     virtual bool execute()
7243         {
7244         String fullName = parent.resolve(fileName);
7245         //trace("fullDir:%s", fullDir.c_str());
7246         String cmd;
7247         String outbuf, errbuf;
7249         if (symFileName.size()>0)
7250             {
7251             String symFullName = parent.resolve(symFileName);
7252             cmd = "objcopy --only-keep-debug ";
7253             cmd.append(getNativePath(fullName));
7254             cmd.append(" ");
7255             cmd.append(getNativePath(symFullName));
7256             if (!executeCommand(cmd, "", outbuf, errbuf))
7257                 {
7258                 error("<strip> symbol file failed : %s", errbuf.c_str());
7259                 return false;
7260                 }
7261             }
7262             
7263         cmd = "strip ";
7264         cmd.append(getNativePath(fullName));
7265         if (!executeCommand(cmd, "", outbuf, errbuf))
7266             {
7267             error("<strip> failed : %s", errbuf.c_str());
7268             return false;
7269             }
7270         return true;
7271         }
7273     virtual bool parse(Element *elem)
7274         {
7275         if (!parent.getAttribute(elem, "file", fileName))
7276             return false;
7277         if (!parent.getAttribute(elem, "symfile", symFileName))
7278             return false;
7279         if (fileName.size() == 0)
7280             {
7281             error("<strip> requires 'file=\"fileName\"' attribute");
7282             return false;
7283             }
7284         return true;
7285         }
7287 private:
7289     String fileName;
7290     String symFileName;
7291 };
7294 /**
7295  *
7296  */
7297 class TaskTouch : public Task
7299 public:
7301     TaskTouch(MakeBase &par) : Task(par)
7302         { type = TASK_TOUCH; name = "touch"; }
7304     virtual ~TaskTouch()
7305         {}
7307     virtual bool execute()
7308         {
7309         String fullName = parent.resolve(fileName);
7310         String nativeFile = getNativePath(fullName);
7311         if (!isRegularFile(fullName) && !isDirectory(fullName))
7312             {            
7313             // S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH
7314             int ret = creat(nativeFile.c_str(), 0666);
7315             if (ret != 0) 
7316                 {
7317                 error("<touch> could not create '%s' : %s",
7318                     nativeFile.c_str(), strerror(ret));
7319                 return false;
7320                 }
7321             return true;
7322             }
7323         int ret = utime(nativeFile.c_str(), (struct utimbuf *)0);
7324         if (ret != 0)
7325             {
7326             error("<touch> could not update the modification time for '%s' : %s",
7327                 nativeFile.c_str(), strerror(ret));
7328             return false;
7329             }
7330         return true;
7331         }
7333     virtual bool parse(Element *elem)
7334         {
7335         //trace("touch parse");
7336         if (!parent.getAttribute(elem, "file", fileName))
7337             return false;
7338         if (fileName.size() == 0)
7339             {
7340             error("<touch> requires 'file=\"fileName\"' attribute");
7341             return false;
7342             }
7343         return true;
7344         }
7346     String fileName;
7347 };
7350 /**
7351  *
7352  */
7353 class TaskTstamp : public Task
7355 public:
7357     TaskTstamp(MakeBase &par) : Task(par)
7358         { type = TASK_TSTAMP; name = "tstamp"; }
7360     virtual ~TaskTstamp()
7361         {}
7363     virtual bool execute()
7364         {
7365         return true;
7366         }
7368     virtual bool parse(Element *elem)
7369         {
7370         //trace("tstamp parse");
7371         return true;
7372         }
7373 };
7377 /**
7378  *
7379  */
7380 Task *Task::createTask(Element *elem)
7382     String tagName = elem->getName();
7383     //trace("task:%s", tagName.c_str());
7384     Task *task = NULL;
7385     if (tagName == "cc")
7386         task = new TaskCC(parent);
7387     else if (tagName == "copy")
7388         task = new TaskCopy(parent);
7389     else if (tagName == "delete")
7390         task = new TaskDelete(parent);
7391     else if (tagName == "jar")
7392         task = new TaskJar(parent);
7393     else if (tagName == "javac")
7394         task = new TaskJavac(parent);
7395     else if (tagName == "link")
7396         task = new TaskLink(parent);
7397     else if (tagName == "makefile")
7398         task = new TaskMakeFile(parent);
7399     else if (tagName == "mkdir")
7400         task = new TaskMkDir(parent);
7401     else if (tagName == "msgfmt")
7402         task = new TaskMsgFmt(parent);
7403     else if (tagName == "ranlib")
7404         task = new TaskRanlib(parent);
7405     else if (tagName == "rc")
7406         task = new TaskRC(parent);
7407     else if (tagName == "sharedlib")
7408         task = new TaskSharedLib(parent);
7409     else if (tagName == "staticlib")
7410         task = new TaskStaticLib(parent);
7411     else if (tagName == "strip")
7412         task = new TaskStrip(parent);
7413     else if (tagName == "touch")
7414         task = new TaskTouch(parent);
7415     else if (tagName == "tstamp")
7416         task = new TaskTstamp(parent);
7417     else
7418         {
7419         error("Unknown task '%s'", tagName.c_str());
7420         return NULL;
7421         }
7423     if (!task->parse(elem))
7424         {
7425         delete task;
7426         return NULL;
7427         }
7428     return task;
7433 //########################################################################
7434 //# T A R G E T
7435 //########################################################################
7437 /**
7438  *
7439  */
7440 class Target : public MakeBase
7443 public:
7445     /**
7446      *
7447      */
7448     Target(Make &par) : parent(par)
7449         { init(); }
7451     /**
7452      *
7453      */
7454     Target(const Target &other) : parent(other.parent)
7455         { init(); assign(other); }
7457     /**
7458      *
7459      */
7460     Target &operator=(const Target &other)
7461         { init(); assign(other); return *this; }
7463     /**
7464      *
7465      */
7466     virtual ~Target()
7467         { cleanup() ; }
7470     /**
7471      *
7472      */
7473     virtual Make &getParent()
7474         { return parent; }
7476     /**
7477      *
7478      */
7479     virtual String getName()
7480         { return name; }
7482     /**
7483      *
7484      */
7485     virtual void setName(const String &val)
7486         { name = val; }
7488     /**
7489      *
7490      */
7491     virtual String getDescription()
7492         { return description; }
7494     /**
7495      *
7496      */
7497     virtual void setDescription(const String &val)
7498         { description = val; }
7500     /**
7501      *
7502      */
7503     virtual void addDependency(const String &val)
7504         { deps.push_back(val); }
7506     /**
7507      *
7508      */
7509     virtual void parseDependencies(const String &val)
7510         { deps = tokenize(val, ", "); }
7512     /**
7513      *
7514      */
7515     virtual std::vector<String> &getDependencies()
7516         { return deps; }
7518     /**
7519      *
7520      */
7521     virtual String getIf()
7522         { return ifVar; }
7524     /**
7525      *
7526      */
7527     virtual void setIf(const String &val)
7528         { ifVar = val; }
7530     /**
7531      *
7532      */
7533     virtual String getUnless()
7534         { return unlessVar; }
7536     /**
7537      *
7538      */
7539     virtual void setUnless(const String &val)
7540         { unlessVar = val; }
7542     /**
7543      *
7544      */
7545     virtual void addTask(Task *val)
7546         { tasks.push_back(val); }
7548     /**
7549      *
7550      */
7551     virtual std::vector<Task *> &getTasks()
7552         { return tasks; }
7554 private:
7556     void init()
7557         {
7558         }
7560     void cleanup()
7561         {
7562         tasks.clear();
7563         }
7565     void assign(const Target &other)
7566         {
7567         //parent      = other.parent;
7568         name        = other.name;
7569         description = other.description;
7570         ifVar       = other.ifVar;
7571         unlessVar   = other.unlessVar;
7572         deps        = other.deps;
7573         tasks       = other.tasks;
7574         }
7576     Make &parent;
7578     String name;
7580     String description;
7582     String ifVar;
7584     String unlessVar;
7586     std::vector<String> deps;
7588     std::vector<Task *> tasks;
7590 };
7599 //########################################################################
7600 //# M A K E
7601 //########################################################################
7604 /**
7605  *
7606  */
7607 class Make : public MakeBase
7610 public:
7612     /**
7613      *
7614      */
7615     Make()
7616         { init(); }
7618     /**
7619      *
7620      */
7621     Make(const Make &other)
7622         { assign(other); }
7624     /**
7625      *
7626      */
7627     Make &operator=(const Make &other)
7628         { assign(other); return *this; }
7630     /**
7631      *
7632      */
7633     virtual ~Make()
7634         { cleanup(); }
7636     /**
7637      *
7638      */
7639     virtual std::map<String, Target> &getTargets()
7640         { return targets; }
7643     /**
7644      *
7645      */
7646     virtual String version()
7647         { return BUILDTOOL_VERSION; }
7649     /**
7650      * Overload a <property>
7651      */
7652     virtual bool specifyProperty(const String &name,
7653                                  const String &value);
7655     /**
7656      *
7657      */
7658     virtual bool run();
7660     /**
7661      *
7662      */
7663     virtual bool run(const String &target);
7667 private:
7669     /**
7670      *
7671      */
7672     void init();
7674     /**
7675      *
7676      */
7677     void cleanup();
7679     /**
7680      *
7681      */
7682     void assign(const Make &other);
7684     /**
7685      *
7686      */
7687     bool executeTask(Task &task);
7690     /**
7691      *
7692      */
7693     bool executeTarget(Target &target,
7694              std::set<String> &targetsCompleted);
7697     /**
7698      *
7699      */
7700     bool execute();
7702     /**
7703      *
7704      */
7705     bool checkTargetDependencies(Target &prop,
7706                     std::vector<String> &depList);
7708     /**
7709      *
7710      */
7711     bool parsePropertyFile(const String &fileName,
7712                            const String &prefix);
7714     /**
7715      *
7716      */
7717     bool parseProperty(Element *elem);
7719     /**
7720      *
7721      */
7722     bool parseTask(Task &task, Element *elem);
7724     /**
7725      *
7726      */
7727     bool parseFile();
7729     /**
7730      *
7731      */
7732     std::vector<String> glob(const String &pattern);
7735     //###############
7736     //# Fields
7737     //###############
7739     String projectName;
7741     String currentTarget;
7743     String defaultTarget;
7745     String specifiedTarget;
7747     String baseDir;
7749     String description;
7750     
7751     String envAlias;
7753     //std::vector<Property> properties;
7754     
7755     std::map<String, Target> targets;
7757     std::vector<Task *> allTasks;
7758     
7759     std::map<String, String> specifiedProperties;
7761 };
7764 //########################################################################
7765 //# C L A S S  M A I N T E N A N C E
7766 //########################################################################
7768 /**
7769  *
7770  */
7771 void Make::init()
7773     uri             = "build.xml";
7774     projectName     = "";
7775     currentTarget   = "";
7776     defaultTarget   = "";
7777     specifiedTarget = "";
7778     baseDir         = "";
7779     description     = "";
7780     envAlias        = "";
7781     properties.clear();
7782     for (unsigned int i = 0 ; i < allTasks.size() ; i++)
7783         delete allTasks[i];
7784     allTasks.clear();
7789 /**
7790  *
7791  */
7792 void Make::cleanup()
7794     for (unsigned int i = 0 ; i < allTasks.size() ; i++)
7795         delete allTasks[i];
7796     allTasks.clear();
7801 /**
7802  *
7803  */
7804 void Make::assign(const Make &other)
7806     uri              = other.uri;
7807     projectName      = other.projectName;
7808     currentTarget    = other.currentTarget;
7809     defaultTarget    = other.defaultTarget;
7810     specifiedTarget  = other.specifiedTarget;
7811     baseDir          = other.baseDir;
7812     description      = other.description;
7813     properties       = other.properties;
7818 //########################################################################
7819 //# U T I L I T Y    T A S K S
7820 //########################################################################
7822 /**
7823  *  Perform a file globbing
7824  */
7825 std::vector<String> Make::glob(const String &pattern)
7827     std::vector<String> res;
7828     return res;
7832 //########################################################################
7833 //# P U B L I C    A P I
7834 //########################################################################
7838 /**
7839  *
7840  */
7841 bool Make::executeTarget(Target &target,
7842              std::set<String> &targetsCompleted)
7845     String name = target.getName();
7847     //First get any dependencies for this target
7848     std::vector<String> deps = target.getDependencies();
7849     for (unsigned int i=0 ; i<deps.size() ; i++)
7850         {
7851         String dep = deps[i];
7852         //Did we do it already?  Skip
7853         if (targetsCompleted.find(dep)!=targetsCompleted.end())
7854             continue;
7855             
7856         std::map<String, Target> &tgts =
7857                target.getParent().getTargets();
7858         std::map<String, Target>::iterator iter =
7859                tgts.find(dep);
7860         if (iter == tgts.end())
7861             {
7862             error("Target '%s' dependency '%s' not found",
7863                       name.c_str(),  dep.c_str());
7864             return false;
7865             }
7866         Target depTarget = iter->second;
7867         if (!executeTarget(depTarget, targetsCompleted))
7868             {
7869             return false;
7870             }
7871         }
7873     status("## Target : %s", name.c_str());
7875     //Now let's do the tasks
7876     std::vector<Task *> &tasks = target.getTasks();
7877     for (unsigned int i=0 ; i<tasks.size() ; i++)
7878         {
7879         Task *task = tasks[i];
7880         status("---- task : %s", task->getName().c_str());
7881         if (!task->execute())
7882             {
7883             return false;
7884             }
7885         }
7886         
7887     targetsCompleted.insert(name);
7888     
7889     return true;
7894 /**
7895  *  Main execute() method.  Start here and work
7896  *  up the dependency tree 
7897  */
7898 bool Make::execute()
7900     status("######## EXECUTE");
7902     //Determine initial target
7903     if (specifiedTarget.size()>0)
7904         {
7905         currentTarget = specifiedTarget;
7906         }
7907     else if (defaultTarget.size()>0)
7908         {
7909         currentTarget = defaultTarget;
7910         }
7911     else
7912         {
7913         error("execute: no specified or default target requested");
7914         return false;
7915         }
7917     std::map<String, Target>::iterator iter =
7918                targets.find(currentTarget);
7919     if (iter == targets.end())
7920         {
7921         error("Initial target '%s' not found",
7922                  currentTarget.c_str());
7923         return false;
7924         }
7925         
7926     //Now run
7927     Target target = iter->second;
7928     std::set<String> targetsCompleted;
7929     if (!executeTarget(target, targetsCompleted))
7930         {
7931         return false;
7932         }
7934     status("######## EXECUTE COMPLETE");
7935     return true;
7941 /**
7942  *
7943  */
7944 bool Make::checkTargetDependencies(Target &target, 
7945                             std::vector<String> &depList)
7947     String tgtName = target.getName().c_str();
7948     depList.push_back(tgtName);
7950     std::vector<String> deps = target.getDependencies();
7951     for (unsigned int i=0 ; i<deps.size() ; i++)
7952         {
7953         String dep = deps[i];
7954         //First thing entered was the starting Target
7955         if (dep == depList[0])
7956             {
7957             error("Circular dependency '%s' found at '%s'",
7958                       dep.c_str(), tgtName.c_str());
7959             std::vector<String>::iterator diter;
7960             for (diter=depList.begin() ; diter!=depList.end() ; diter++)
7961                 {
7962                 error("  %s", diter->c_str());
7963                 }
7964             return false;
7965             }
7967         std::map<String, Target> &tgts =
7968                   target.getParent().getTargets();
7969         std::map<String, Target>::iterator titer = tgts.find(dep);
7970         if (titer == tgts.end())
7971             {
7972             error("Target '%s' dependency '%s' not found",
7973                       tgtName.c_str(), dep.c_str());
7974             return false;
7975             }
7976         if (!checkTargetDependencies(titer->second, depList))
7977             {
7978             return false;
7979             }
7980         }
7981     return true;
7988 static int getword(int pos, const String &inbuf, String &result)
7990     int p = pos;
7991     int len = (int)inbuf.size();
7992     String val;
7993     while (p < len)
7994         {
7995         char ch = inbuf[p];
7996         if (!isalnum(ch) && ch!='.' && ch!='_')
7997             break;
7998         val.push_back(ch);
7999         p++;
8000         }
8001     result = val;
8002     return p;
8008 /**
8009  *
8010  */
8011 bool Make::parsePropertyFile(const String &fileName,
8012                              const String &prefix)
8014     FILE *f = fopen(fileName.c_str(), "r");
8015     if (!f)
8016         {
8017         error("could not open property file %s", fileName.c_str());
8018         return false;
8019         }
8020     int linenr = 0;
8021     while (!feof(f))
8022         {
8023         char buf[256];
8024         if (!fgets(buf, 255, f))
8025             break;
8026         linenr++;
8027         String s = buf;
8028         s = trim(s);
8029         int len = s.size();
8030         if (len == 0)
8031             continue;
8032         if (s[0] == '#')
8033             continue;
8034         String key;
8035         String val;
8036         int p = 0;
8037         int p2 = getword(p, s, key);
8038         if (p2 <= p)
8039             {
8040             error("property file %s, line %d: expected keyword",
8041                     fileName.c_str(), linenr);
8042             return false;
8043             }
8044         if (prefix.size() > 0)
8045             {
8046             key.insert(0, prefix);
8047             }
8049         //skip whitespace
8050         for (p=p2 ; p<len ; p++)
8051             if (!isspace(s[p]))
8052                 break;
8054         if (p>=len || s[p]!='=')
8055             {
8056             error("property file %s, line %d: expected '='",
8057                     fileName.c_str(), linenr);
8058             return false;
8059             }
8060         p++;
8062         //skip whitespace
8063         for ( ; p<len ; p++)
8064             if (!isspace(s[p]))
8065                 break;
8067         /* This way expects a word after the =
8068         p2 = getword(p, s, val);
8069         if (p2 <= p)
8070             {
8071             error("property file %s, line %d: expected value",
8072                     fileName.c_str(), linenr);
8073             return false;
8074             }
8075         */
8076         // This way gets the rest of the line after the =
8077         if (p>=len)
8078             {
8079             error("property file %s, line %d: expected value",
8080                     fileName.c_str(), linenr);
8081             return false;
8082             }
8083         val = s.substr(p);
8084         if (key.size()==0)
8085             continue;
8086         //allow property to be set, even if val=""
8088         //trace("key:'%s' val:'%s'", key.c_str(), val.c_str());
8089         //See if we wanted to overload this property
8090         std::map<String, String>::iterator iter =
8091             specifiedProperties.find(key);
8092         if (iter!=specifiedProperties.end())
8093             {
8094             val = iter->second;
8095             status("overloading property '%s' = '%s'",
8096                    key.c_str(), val.c_str());
8097             }
8098         properties[key] = val;
8099         }
8100     fclose(f);
8101     return true;
8107 /**
8108  *
8109  */
8110 bool Make::parseProperty(Element *elem)
8112     std::vector<Attribute> &attrs = elem->getAttributes();
8113     for (unsigned int i=0 ; i<attrs.size() ; i++)
8114         {
8115         String attrName = attrs[i].getName();
8116         String attrVal  = attrs[i].getValue();
8118         if (attrName == "name")
8119             {
8120             String val;
8121             if (!getAttribute(elem, "value", val))
8122                 return false;
8123             if (val.size() > 0)
8124                 {
8125                 properties[attrVal] = val;
8126                 }
8127             else
8128                 {
8129                 if (!getAttribute(elem, "location", val))
8130                     return false;
8131                 //let the property exist, even if not defined
8132                 properties[attrVal] = val;
8133                 }
8134             //See if we wanted to overload this property
8135             std::map<String, String>::iterator iter =
8136                 specifiedProperties.find(attrVal);
8137             if (iter != specifiedProperties.end())
8138                 {
8139                 val = iter->second;
8140                 status("overloading property '%s' = '%s'",
8141                     attrVal.c_str(), val.c_str());
8142                 properties[attrVal] = val;
8143                 }
8144             }
8145         else if (attrName == "file")
8146             {
8147             String prefix;
8148             if (!getAttribute(elem, "prefix", prefix))
8149                 return false;
8150             if (prefix.size() > 0)
8151                 {
8152                 if (prefix[prefix.size()-1] != '.')
8153                     prefix.push_back('.');
8154                 }
8155             if (!parsePropertyFile(attrName, prefix))
8156                 return false;
8157             }
8158         else if (attrName == "environment")
8159             {
8160             if (envAlias.size() > 0)
8161                 {
8162                 error("environment property can only be set once");
8163                 return false;
8164                 }
8165             envAlias = attrVal;
8166             }
8167         }
8169     return true;
8175 /**
8176  *
8177  */
8178 bool Make::parseFile()
8180     status("######## PARSE : %s", uri.getPath().c_str());
8182     Parser parser;
8183     Element *root = parser.parseFile(uri.getNativePath());
8184     if (!root)
8185         {
8186         error("Could not open %s for reading",
8187               uri.getNativePath().c_str());
8188         return false;
8189         }
8191     if (root->getChildren().size()==0 ||
8192         root->getChildren()[0]->getName()!="project")
8193         {
8194         error("Main xml element should be <project>");
8195         delete root;
8196         return false;
8197         }
8199     //########## Project attributes
8200     Element *project = root->getChildren()[0];
8201     String s = project->getAttribute("name");
8202     if (s.size() > 0)
8203         projectName = s;
8204     s = project->getAttribute("default");
8205     if (s.size() > 0)
8206         defaultTarget = s;
8207     s = project->getAttribute("basedir");
8208     if (s.size() > 0)
8209         baseDir = s;
8211     //######### PARSE MEMBERS
8212     std::vector<Element *> children = project->getChildren();
8213     for (unsigned int i=0 ; i<children.size() ; i++)
8214         {
8215         Element *elem = children[i];
8216         String tagName = elem->getName();
8218         //########## DESCRIPTION
8219         if (tagName == "description")
8220             {
8221             description = parser.trim(elem->getValue());
8222             }
8224         //######### PROPERTY
8225         else if (tagName == "property")
8226             {
8227             if (!parseProperty(elem))
8228                 return false;
8229             }
8231         //######### TARGET
8232         else if (tagName == "target")
8233             {
8234             String tname   = elem->getAttribute("name");
8235             String tdesc   = elem->getAttribute("description");
8236             String tdeps   = elem->getAttribute("depends");
8237             String tif     = elem->getAttribute("if");
8238             String tunless = elem->getAttribute("unless");
8239             Target target(*this);
8240             target.setName(tname);
8241             target.setDescription(tdesc);
8242             target.parseDependencies(tdeps);
8243             target.setIf(tif);
8244             target.setUnless(tunless);
8245             std::vector<Element *> telems = elem->getChildren();
8246             for (unsigned int i=0 ; i<telems.size() ; i++)
8247                 {
8248                 Element *telem = telems[i];
8249                 Task breeder(*this);
8250                 Task *task = breeder.createTask(telem);
8251                 if (!task)
8252                     return false;
8253                 allTasks.push_back(task);
8254                 target.addTask(task);
8255                 }
8257             //Check name
8258             if (tname.size() == 0)
8259                 {
8260                 error("no name for target");
8261                 return false;
8262                 }
8263             //Check for duplicate name
8264             if (targets.find(tname) != targets.end())
8265                 {
8266                 error("target '%s' already defined", tname.c_str());
8267                 return false;
8268                 }
8269             //more work than targets[tname]=target, but avoids default allocator
8270             targets.insert(std::make_pair<String, Target>(tname, target));
8271             }
8273         }
8275     std::map<String, Target>::iterator iter;
8276     for (iter = targets.begin() ; iter!= targets.end() ; iter++)
8277         {
8278         Target tgt = iter->second;
8279         std::vector<String> depList;
8280         if (!checkTargetDependencies(tgt, depList))
8281             {
8282             return false;
8283             }
8284         }
8287     delete root;
8288     status("######## PARSE COMPLETE");
8289     return true;
8293 /**
8294  * Overload a <property>
8295  */
8296 bool Make::specifyProperty(const String &name, const String &value)
8298     if (specifiedProperties.find(name) != specifiedProperties.end())
8299         {
8300         error("Property %s already specified", name.c_str());
8301         return false;
8302         }
8303     specifiedProperties[name] = value;
8304     return true;
8309 /**
8310  *
8311  */
8312 bool Make::run()
8314     if (!parseFile())
8315         return false;
8316         
8317     if (!execute())
8318         return false;
8320     return true;
8326 /**
8327  * Get a formatted MM:SS.sss time elapsed string
8328  */ 
8329 static String
8330 timeDiffString(struct timeval &x, struct timeval &y)
8332     long microsX  = x.tv_usec;
8333     long secondsX = x.tv_sec;
8334     long microsY  = y.tv_usec;
8335     long secondsY = y.tv_sec;
8336     if (microsX < microsY)
8337         {
8338         microsX += 1000000;
8339         secondsX -= 1;
8340         }
8342     int seconds = (int)(secondsX - secondsY);
8343     int millis  = (int)((microsX - microsY)/1000);
8345     int minutes = seconds/60;
8346     seconds -= minutes*60;
8347     char buf[80];
8348     snprintf(buf, 79, "%dm %d.%03ds", minutes, seconds, millis);
8349     String ret = buf;
8350     return ret;
8351     
8354 /**
8355  *
8356  */
8357 bool Make::run(const String &target)
8359     status("####################################################");
8360     status("#   %s", version().c_str());
8361     status("####################################################");
8362     struct timeval timeStart, timeEnd;
8363     ::gettimeofday(&timeStart, NULL);
8364     specifiedTarget = target;
8365     if (!run())
8366         return false;
8367     ::gettimeofday(&timeEnd, NULL);
8368     String timeStr = timeDiffString(timeEnd, timeStart);
8369     status("####################################################");
8370     status("#   BuildTool Completed : %s", timeStr.c_str());
8371     status("####################################################");
8372     return true;
8381 }// namespace buildtool
8382 //########################################################################
8383 //# M A I N
8384 //########################################################################
8386 typedef buildtool::String String;
8388 /**
8389  *  Format an error message in printf() style
8390  */
8391 static void error(char *fmt, ...)
8393     va_list ap;
8394     va_start(ap, fmt);
8395     fprintf(stderr, "BuildTool error: ");
8396     vfprintf(stderr, fmt, ap);
8397     fprintf(stderr, "\n");
8398     va_end(ap);
8402 static bool parseProperty(const String &s, String &name, String &val)
8404     int len = s.size();
8405     int i;
8406     for (i=0 ; i<len ; i++)
8407         {
8408         char ch = s[i];
8409         if (ch == '=')
8410             break;
8411         name.push_back(ch);
8412         }
8413     if (i>=len || s[i]!='=')
8414         {
8415         error("property requires -Dname=value");
8416         return false;
8417         }
8418     i++;
8419     for ( ; i<len ; i++)
8420         {
8421         char ch = s[i];
8422         val.push_back(ch);
8423         }
8424     return true;
8428 /**
8429  * Compare a buffer with a key, for the length of the key
8430  */
8431 static bool sequ(const String &buf, char *key)
8433     int len = buf.size();
8434     for (int i=0 ; key[i] && i<len ; i++)
8435         {
8436         if (key[i] != buf[i])
8437             return false;
8438         }        
8439     return true;
8442 static void usage(int argc, char **argv)
8444     printf("usage:\n");
8445     printf("   %s [options] [target]\n", argv[0]);
8446     printf("Options:\n");
8447     printf("  -help, -h              print this message\n");
8448     printf("  -version               print the version information and exit\n");
8449     printf("  -file <file>           use given buildfile\n");
8450     printf("  -f <file>                 ''\n");
8451     printf("  -D<property>=<value>   use value for given property\n");
8457 /**
8458  * Parse the command-line args, get our options,
8459  * and run this thing
8460  */   
8461 static bool parseOptions(int argc, char **argv)
8463     if (argc < 1)
8464         {
8465         error("Cannot parse arguments");
8466         return false;
8467         }
8469     buildtool::Make make;
8471     String target;
8473     //char *progName = argv[0];
8474     for (int i=1 ; i<argc ; i++)
8475         {
8476         String arg = argv[i];
8477         if (arg.size()>1 && arg[0]=='-')
8478             {
8479             if (arg == "-h" || arg == "-help")
8480                 {
8481                 usage(argc,argv);
8482                 return true;
8483                 }
8484             else if (arg == "-version")
8485                 {
8486                 printf("%s", make.version().c_str());
8487                 return true;
8488                 }
8489             else if (arg == "-f" || arg == "-file")
8490                 {
8491                 if (i>=argc)
8492                    {
8493                    usage(argc, argv);
8494                    return false;
8495                    }
8496                 i++; //eat option
8497                 make.setURI(argv[i]);
8498                 }
8499             else if (arg.size()>2 && sequ(arg, "-D"))
8500                 {
8501                 String s = arg.substr(2, s.size());
8502                 String name, value;
8503                 if (!parseProperty(s, name, value))
8504                    {
8505                    usage(argc, argv);
8506                    return false;
8507                    }
8508                 if (!make.specifyProperty(name, value))
8509                     return false;
8510                 }
8511             else
8512                 {
8513                 error("Unknown option:%s", arg.c_str());
8514                 return false;
8515                 }
8516             }
8517         else
8518             {
8519             if (target.size()>0)
8520                 {
8521                 error("only one initial target");
8522                 usage(argc, argv);
8523                 return false;
8524                 }
8525             target = arg;
8526             }
8527         }
8529     //We have the options.  Now execute them
8530     if (!make.run(target))
8531         return false;
8533     return true;
8539 /*
8540 static bool runMake()
8542     buildtool::Make make;
8543     if (!make.run())
8544         return false;
8545     return true;
8549 static bool pkgConfigTest()
8551     buildtool::PkgConfig pkgConfig;
8552     if (!pkgConfig.readFile("gtk+-2.0.pc"))
8553         return false;
8554     return true;
8559 static bool depTest()
8561     buildtool::DepTool deptool;
8562     deptool.setSourceDirectory("/dev/ink/inkscape/src");
8563     if (!deptool.generateDependencies("build.dep"))
8564         return false;
8565     std::vector<buildtool::DepRec> res =
8566            deptool.loadDepFile("build.dep");
8567     if (res.size() == 0)
8568         return false;
8569     return true;
8572 static bool popenTest()
8574     buildtool::Make make;
8575     buildtool::String out, err;
8576     bool ret = make.executeCommand("gcc xx.cpp", "", out, err);
8577     printf("Popen test:%d '%s' '%s'\n", ret, out.c_str(), err.c_str());
8578     return true;
8582 static bool propFileTest()
8584     buildtool::Make make;
8585     make.parsePropertyFile("test.prop", "test.");
8586     return true;
8588 */
8590 int main(int argc, char **argv)
8593     if (!parseOptions(argc, argv))
8594         return 1;
8595     /*
8596     if (!popenTest())
8597         return 1;
8599     if (!depTest())
8600         return 1;
8601     if (!propFileTest())
8602         return 1;
8603     if (runMake())
8604         return 1;
8605     */
8606     return 0;
8610 //########################################################################
8611 //# E N D 
8612 //########################################################################