Code

Add <touch> . Fix piping again.
[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.3, 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     //# Create pipes
3402     SECURITY_ATTRIBUTES saAttr; 
3403     saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); 
3404     saAttr.bInheritHandle = TRUE; 
3405     saAttr.lpSecurityDescriptor = NULL; 
3406     HANDLE stdinRead,  stdinWrite;
3407     HANDLE stdoutRead, stdoutWrite;
3408     HANDLE stderrRead, stderrWrite;
3409     if (!CreatePipe(&stdinRead, &stdinWrite, &saAttr, 0))
3410         {
3411         error("executeProgram: could not create pipe");
3412         delete[] paramBuf;
3413         return false;
3414         } 
3415     SetHandleInformation(stdinWrite, HANDLE_FLAG_INHERIT, 0);
3416     if (!CreatePipe(&stdoutRead, &stdoutWrite, &saAttr, 0))
3417         {
3418         error("executeProgram: could not create pipe");
3419         delete[] paramBuf;
3420         return false;
3421         } 
3422     SetHandleInformation(stdoutRead, HANDLE_FLAG_INHERIT, 0);
3423     if (!CreatePipe(&stderrRead, &stderrWrite, &saAttr, 0))
3424         {
3425         error("executeProgram: could not create pipe");
3426         delete[] paramBuf;
3427         return false;
3428         } 
3429     SetHandleInformation(stderrRead, HANDLE_FLAG_INHERIT, 0);
3431     // Create the process
3432     STARTUPINFO siStartupInfo;
3433     PROCESS_INFORMATION piProcessInfo;
3434     memset(&siStartupInfo, 0, sizeof(siStartupInfo));
3435     memset(&piProcessInfo, 0, sizeof(piProcessInfo));
3436     siStartupInfo.cb = sizeof(siStartupInfo);
3437     siStartupInfo.hStdError   =  stderrWrite;
3438     siStartupInfo.hStdOutput  =  stdoutWrite;
3439     siStartupInfo.hStdInput   =  stdinRead;
3440     siStartupInfo.dwFlags    |=  STARTF_USESTDHANDLES;
3441    
3442     if (!CreateProcess(NULL, paramBuf, NULL, NULL, true,
3443                 0, NULL, NULL, &siStartupInfo,
3444                 &piProcessInfo))
3445         {
3446         error("executeCommand : could not create process : %s",
3447                     win32LastError().c_str());
3448         ret = false;
3449         }
3451     DWORD bytesWritten;
3452     if (inbuf.size()>0 &&
3453         !WriteFile(stdinWrite, inbuf.c_str(), inbuf.size(), 
3454                &bytesWritten, NULL))
3455         {
3456         error("executeCommand: could not write to pipe");
3457         return false;
3458         }    
3459     if (!CloseHandle(stdinWrite))
3460         {          
3461         error("executeCommand: could not close write pipe");
3462         return false;
3463         }
3464     if (!CloseHandle(stdoutWrite))
3465         {
3466         error("executeCommand: could not close read pipe");
3467         return false;
3468         }
3469     if (!CloseHandle(stderrWrite))
3470         {
3471         error("executeCommand: could not close read pipe");
3472         return false;
3473         }
3474     while (true)
3475         {
3476         //trace("## stderr");
3477         DWORD avail;
3478         PeekNamedPipe(stderrRead, NULL, 0, NULL, &avail, NULL);
3479         if (avail > 0)
3480             {
3481             DWORD bytesRead = 0;
3482             char readBuf[1025];
3483             if (avail>1024) avail = 1024;
3484             ReadFile(stderrRead, readBuf, avail, &bytesRead, NULL);
3485             if (bytesRead > 0)
3486                 {
3487                 for (unsigned int i=0 ; i<bytesRead ; i++)
3488                     errbuf.push_back(readBuf[i]);
3489                 }
3490             }
3492         //trace("## stdout");
3493         PeekNamedPipe(stdoutRead, NULL, 0, NULL, &avail, NULL);
3494         if (avail > 0)
3495             {
3496             DWORD bytesRead = 0;
3497             char readBuf[1025];
3498             if (avail>1024) avail = 1024;
3499             ReadFile(stdoutRead, readBuf, avail, &bytesRead, NULL);
3500             if (bytesRead > 0)
3501                 {
3502                 for (unsigned int i=0 ; i<bytesRead ; i++)
3503                     outbuf.push_back(readBuf[i]);
3504                 }
3505             }
3506         DWORD exitCode;
3507         GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
3508         if (exitCode != STILL_ACTIVE)
3509             break;
3510         Sleep(100);
3511         }    
3512     //trace("outbuf:%s", outbuf.c_str());
3513     if (!CloseHandle(stdoutRead))
3514         {
3515         error("executeCommand: could not close read pipe");
3516         return false;
3517         }
3518     if (!CloseHandle(stderrRead))
3519         {
3520         error("executeCommand: could not close read pipe");
3521         return false;
3522         }
3524     DWORD exitCode;
3525     GetExitCodeProcess(piProcessInfo.hProcess, &exitCode);
3526     //trace("exit code:%d", exitCode);
3527     if (exitCode != 0)
3528         {
3529         ret = false;
3530         }
3531     
3532     // Clean up
3533     CloseHandle(piProcessInfo.hProcess);
3534     CloseHandle(piProcessInfo.hThread);
3537     return ret;
3539 #else //do it unix-style
3541     String s;
3542     FILE *f = popen(command.c_str(), "r");
3543     int errnum = 0;
3544     if (f)
3545         {
3546         while (true)
3547             {
3548             int ch = fgetc(f);
3549             if (ch < 0)
3550                 break;
3551             s.push_back((char)ch);
3552             }
3553         errnum = pclose(f);
3554         }
3555     outbuf = s;
3556     if (errnum != 0)
3557         {
3558         error("exec of command '%s' failed : %s",
3559              command.c_str(), strerror(errno));
3560         return false;
3561         }
3562     else
3563         return true;
3565 #endif
3566
3571 bool MakeBase::listDirectories(const String &baseName,
3572                               const String &dirName,
3573                               std::vector<String> &res)
3575     res.push_back(dirName);
3576     String fullPath = baseName;
3577     if (dirName.size()>0)
3578         {
3579         fullPath.append("/");
3580         fullPath.append(dirName);
3581         }
3582     DIR *dir = opendir(fullPath.c_str());
3583     while (true)
3584         {
3585         struct dirent *de = readdir(dir);
3586         if (!de)
3587             break;
3589         //Get the directory member name
3590         String s = de->d_name;
3591         if (s.size() == 0 || s[0] == '.')
3592             continue;
3593         String childName = dirName;
3594         childName.append("/");
3595         childName.append(s);
3597         String fullChildPath = baseName;
3598         fullChildPath.append("/");
3599         fullChildPath.append(childName);
3600         struct stat finfo;
3601         String childNative = getNativePath(fullChildPath);
3602         if (stat(childNative.c_str(), &finfo)<0)
3603             {
3604             error("cannot stat file:%s", childNative.c_str());
3605             }
3606         else if (S_ISDIR(finfo.st_mode))
3607             {
3608             //trace("directory: %s", childName.c_str());
3609             if (!listDirectories(baseName, childName, res))
3610                 return false;
3611             }
3612         }
3613     closedir(dir);
3615     return true;
3619 bool MakeBase::listFiles(const String &baseDir,
3620                          const String &dirName,
3621                          std::vector<String> &res)
3623     String fullDir = baseDir;
3624     if (dirName.size()>0)
3625         {
3626         fullDir.append("/");
3627         fullDir.append(dirName);
3628         }
3629     String dirNative = getNativePath(fullDir);
3631     std::vector<String> subdirs;
3632     DIR *dir = opendir(dirNative.c_str());
3633     if (!dir)
3634         {
3635         error("Could not open directory %s : %s",
3636               dirNative.c_str(), strerror(errno));
3637         return false;
3638         }
3639     while (true)
3640         {
3641         struct dirent *de = readdir(dir);
3642         if (!de)
3643             break;
3645         //Get the directory member name
3646         String s = de->d_name;
3647         if (s.size() == 0 || s[0] == '.')
3648             continue;
3649         String childName;
3650         if (dirName.size()>0)
3651             {
3652             childName.append(dirName);
3653             childName.append("/");
3654             }
3655         childName.append(s);
3656         String fullChild = baseDir;
3657         fullChild.append("/");
3658         fullChild.append(childName);
3659         
3660         if (isDirectory(fullChild))
3661             {
3662             //trace("directory: %s", childName.c_str());
3663             if (!listFiles(baseDir, childName, res))
3664                 return false;
3665             continue;
3666             }
3667         else if (!isRegularFile(fullChild))
3668             {
3669             error("unknown file:%s", childName.c_str());
3670             return false;
3671             }
3673        //all done!
3674         res.push_back(childName);
3676         }
3677     closedir(dir);
3679     return true;
3683 bool MakeBase::listFiles(MakeBase &propRef, FileSet &fileSet)
3685     String baseDir = propRef.resolve(fileSet.getDirectory());
3686     std::vector<String> fileList;
3687     if (!listFiles(baseDir, "", fileList))
3688         return false;
3690     std::vector<String> includes = fileSet.getIncludes();
3691     std::vector<String> excludes = fileSet.getExcludes();
3693     std::vector<String> incs;
3694     std::vector<String>::iterator iter;
3696     std::sort(fileList.begin(), fileList.end());
3698     //If there are <includes>, then add files to the output
3699     //in the order of the include list
3700     if (includes.size()==0)
3701         incs = fileList;
3702     else
3703         {
3704         for (iter = includes.begin() ; iter != includes.end() ; iter++)
3705             {
3706             String pattern = *iter;
3707             std::vector<String>::iterator siter;
3708             for (siter = fileList.begin() ; siter != fileList.end() ; siter++)
3709                 {
3710                 String s = *siter;
3711                 if (regexMatch(s, pattern))
3712                     {
3713                     //trace("INCLUDED:%s", s.c_str());
3714                     incs.push_back(s);
3715                     }
3716                 }
3717             }
3718         }
3720     //Now trim off the <excludes>
3721     std::vector<String> res;
3722     for (iter = incs.begin() ; iter != incs.end() ; iter++)
3723         {
3724         String s = *iter;
3725         bool skipme = false;
3726         std::vector<String>::iterator siter;
3727         for (siter = excludes.begin() ; siter != excludes.end() ; siter++)
3728             {
3729             String pattern = *siter;
3730             if (regexMatch(s, pattern))
3731                 {
3732                 //trace("EXCLUDED:%s", s.c_str());
3733                 skipme = true;
3734                 break;
3735                 }
3736             }
3737         if (!skipme)
3738             res.push_back(s);
3739         }
3740         
3741     fileSet.setFiles(res);
3743     return true;
3750 bool MakeBase::getSubstitutions(const String &str, String &result)
3752     String s = trim(str);
3753     int len = (int)s.size();
3754     String val;
3755     for (int i=0 ; i<len ; i++)
3756         {
3757         char ch = s[i];
3758         if (ch == '$' && s[i+1] == '{')
3759             {
3760             String varname;
3761             int j = i+2;
3762             for ( ; j<len ; j++)
3763                 {
3764                 ch = s[j];
3765                 if (ch == '$' && s[j+1] == '{')
3766                     {
3767                     error("attribute %s cannot have nested variable references",
3768                            s.c_str());
3769                     return false;
3770                     }
3771                 else if (ch == '}')
3772                     {
3773                     std::map<String, String>::iterator iter;
3774                     iter = properties.find(trim(varname));
3775                     if (iter != properties.end())
3776                         {
3777                         val.append(iter->second);
3778                         }
3779                     else
3780                         {
3781                         error("property ${%s} not found", varname.c_str());
3782                         return false;
3783                         }
3784                     break;
3785                     }
3786                 else
3787                     {
3788                     varname.push_back(ch);
3789                     }
3790                 }
3791             i = j;
3792             }
3793         else
3794             {
3795             val.push_back(ch);
3796             }
3797         }
3798     result = val;
3799     return true;
3803 bool MakeBase::getAttribute(Element *elem, const String &name,
3804                                     String &result)
3806     String s = elem->getAttribute(name);
3807     return getSubstitutions(s, result);
3811 bool MakeBase::getValue(Element *elem, String &result)
3813     String s = elem->getValue();
3814     //Replace all runs of whitespace with a single space
3815     return getSubstitutions(s, result);
3819 /**
3820  * Turn 'true' and 'false' into boolean values
3821  */             
3822 bool MakeBase::getBool(const String &str, bool &val)
3824     if (str == "true")
3825         val = true;
3826     else if (str == "false")
3827         val = false;
3828     else
3829         {
3830         error("expected 'true' or 'false'.  found '%s'", str.c_str());
3831         return false;
3832         }
3833     return true;
3839 /**
3840  * Parse a <patternset> entry
3841  */  
3842 bool MakeBase::parsePatternSet(Element *elem,
3843                           MakeBase &propRef,
3844                           std::vector<String> &includes,
3845                           std::vector<String> &excludes
3846                           )
3848     std::vector<Element *> children  = elem->getChildren();
3849     for (unsigned int i=0 ; i<children.size() ; i++)
3850         {
3851         Element *child = children[i];
3852         String tagName = child->getName();
3853         if (tagName == "exclude")
3854             {
3855             String fname;
3856             if (!propRef.getAttribute(child, "name", fname))
3857                 return false;
3858             //trace("EXCLUDE: %s", fname.c_str());
3859             excludes.push_back(fname);
3860             }
3861         else if (tagName == "include")
3862             {
3863             String fname;
3864             if (!propRef.getAttribute(child, "name", fname))
3865                 return false;
3866             //trace("INCLUDE: %s", fname.c_str());
3867             includes.push_back(fname);
3868             }
3869         }
3871     return true;
3877 /**
3878  * Parse a <fileset> entry, and determine which files
3879  * should be included
3880  */  
3881 bool MakeBase::parseFileSet(Element *elem,
3882                           MakeBase &propRef,
3883                           FileSet &fileSet)
3885     String name = elem->getName();
3886     if (name != "fileset")
3887         {
3888         error("expected <fileset>");
3889         return false;
3890         }
3893     std::vector<String> includes;
3894     std::vector<String> excludes;
3896     //A fileset has one implied patternset
3897     if (!parsePatternSet(elem, propRef, includes, excludes))
3898         {
3899         return false;
3900         }
3901     //Look for child tags, including more patternsets
3902     std::vector<Element *> children  = elem->getChildren();
3903     for (unsigned int i=0 ; i<children.size() ; i++)
3904         {
3905         Element *child = children[i];
3906         String tagName = child->getName();
3907         if (tagName == "patternset")
3908             {
3909             if (!parsePatternSet(child, propRef, includes, excludes))
3910                 {
3911                 return false;
3912                 }
3913             }
3914         }
3916     String dir;
3917     //Now do the stuff
3918     //Get the base directory for reading file names
3919     if (!propRef.getAttribute(elem, "dir", dir))
3920         return false;
3922     fileSet.setDirectory(dir);
3923     fileSet.setIncludes(includes);
3924     fileSet.setExcludes(excludes);
3925     
3926     /*
3927     std::vector<String> fileList;
3928     if (dir.size() > 0)
3929         {
3930         String baseDir = propRef.resolve(dir);
3931         if (!listFiles(baseDir, "", includes, excludes, fileList))
3932             return false;
3933         }
3934     std::sort(fileList.begin(), fileList.end());
3935     result = fileList;
3936     */
3938     
3939     /*
3940     for (unsigned int i=0 ; i<result.size() ; i++)
3941         {
3942         trace("RES:%s", result[i].c_str());
3943         }
3944     */
3946     
3947     return true;
3952 /**
3953  * Create a directory, making intermediate dirs
3954  * if necessary
3955  */                  
3956 bool MakeBase::createDirectory(const String &dirname)
3958     //trace("## createDirectory: %s", dirname.c_str());
3959     //## first check if it exists
3960     struct stat finfo;
3961     String nativeDir = getNativePath(dirname);
3962     char *cnative = (char *) nativeDir.c_str();
3963 #ifdef __WIN32__
3964     if (strlen(cnative)==2 && cnative[1]==':')
3965         return true;
3966 #endif
3967     if (stat(cnative, &finfo)==0)
3968         {
3969         if (!S_ISDIR(finfo.st_mode))
3970             {
3971             error("mkdir: file %s exists but is not a directory",
3972                   cnative);
3973             return false;
3974             }
3975         else //exists
3976             {
3977             return true;
3978             }
3979         }
3981     //## 2: pull off the last path segment, if any,
3982     //## to make the dir 'above' this one, if necessary
3983     unsigned int pos = dirname.find_last_of('/');
3984     if (pos>0 && pos != dirname.npos)
3985         {
3986         String subpath = dirname.substr(0, pos);
3987         //A letter root (c:) ?
3988         if (!createDirectory(subpath))
3989             return false;
3990         }
3991         
3992     //## 3: now make
3993 #ifdef __WIN32__
3994     if (mkdir(cnative)<0)
3995 #else
3996     if (mkdir(cnative, S_IRWXU | S_IRWXG | S_IRWXO)<0)
3997 #endif
3998         {
3999         error("cannot make directory '%s' : %s",
4000                  cnative, strerror(errno));
4001         return false;
4002         }
4003         
4004     return true;
4008 /**
4009  * Remove a directory recursively
4010  */ 
4011 bool MakeBase::removeDirectory(const String &dirName)
4013     char *dname = (char *)dirName.c_str();
4015     DIR *dir = opendir(dname);
4016     if (!dir)
4017         {
4018         //# Let this fail nicely.
4019         return true;
4020         //error("error opening directory %s : %s", dname, strerror(errno));
4021         //return false;
4022         }
4023     
4024     while (true)
4025         {
4026         struct dirent *de = readdir(dir);
4027         if (!de)
4028             break;
4030         //Get the directory member name
4031         String s = de->d_name;
4032         if (s.size() == 0 || s[0] == '.')
4033             continue;
4034         String childName;
4035         if (dirName.size() > 0)
4036             {
4037             childName.append(dirName);
4038             childName.append("/");
4039             }
4040         childName.append(s);
4043         struct stat finfo;
4044         String childNative = getNativePath(childName);
4045         char *cnative = (char *)childNative.c_str();
4046         if (stat(cnative, &finfo)<0)
4047             {
4048             error("cannot stat file:%s", cnative);
4049             }
4050         else if (S_ISDIR(finfo.st_mode))
4051             {
4052             //trace("DEL dir: %s", childName.c_str());
4053             if (!removeDirectory(childName))
4054                 {
4055                 return false;
4056                 }
4057             }
4058         else if (!S_ISREG(finfo.st_mode))
4059             {
4060             //trace("not regular: %s", cnative);
4061             }
4062         else
4063             {
4064             //trace("DEL file: %s", childName.c_str());
4065             if (remove(cnative)<0)
4066                 {
4067                 error("error deleting %s : %s",
4068                      cnative, strerror(errno));
4069                 return false;
4070                 }
4071             }
4072         }
4073     closedir(dir);
4075     //Now delete the directory
4076     String native = getNativePath(dirName);
4077     if (rmdir(native.c_str())<0)
4078         {
4079         error("could not delete directory %s : %s",
4080             native.c_str() , strerror(errno));
4081         return false;
4082         }
4084     return true;
4085     
4089 /**
4090  * Copy a file from one name to another. Perform only if needed
4091  */ 
4092 bool MakeBase::copyFile(const String &srcFile, const String &destFile)
4094     //# 1 Check up-to-date times
4095     String srcNative = getNativePath(srcFile);
4096     struct stat srcinfo;
4097     if (stat(srcNative.c_str(), &srcinfo)<0)
4098         {
4099         error("source file %s for copy does not exist",
4100                  srcNative.c_str());
4101         return false;
4102         }
4104     String destNative = getNativePath(destFile);
4105     struct stat destinfo;
4106     if (stat(destNative.c_str(), &destinfo)==0)
4107         {
4108         if (destinfo.st_mtime >= srcinfo.st_mtime)
4109             return true;
4110         }
4111         
4112     //# 2 prepare a destination directory if necessary
4113     unsigned int pos = destFile.find_last_of('/');
4114     if (pos != destFile.npos)
4115         {
4116         String subpath = destFile.substr(0, pos);
4117         if (!createDirectory(subpath))
4118             return false;
4119         }
4121     //# 3 do the data copy
4122 #ifndef __WIN32__
4124     FILE *srcf = fopen(srcNative.c_str(), "rb");
4125     if (!srcf)
4126         {
4127         error("copyFile cannot open '%s' for reading", srcNative.c_str());
4128         return false;
4129         }
4130     FILE *destf = fopen(destNative.c_str(), "wb");
4131     if (!destf)
4132         {
4133         error("copyFile cannot open %s for writing", srcNative.c_str());
4134         return false;
4135         }
4137     while (!feof(srcf))
4138         {
4139         int ch = fgetc(srcf);
4140         if (ch<0)
4141             break;
4142         fputc(ch, destf);
4143         }
4145     fclose(destf);
4146     fclose(srcf);
4148 #else
4149     
4150     if (!CopyFile(srcNative.c_str(), destNative.c_str(), false))
4151         {
4152         error("copyFile from %s to %s failed",
4153              srcNative.c_str(), destNative.c_str());
4154         return false;
4155         }
4156         
4157 #endif /* __WIN32__ */
4160     return true;
4165 /**
4166  * Tests if the file exists and is a regular file
4167  */ 
4168 bool MakeBase::isRegularFile(const String &fileName)
4170     String native = getNativePath(fileName);
4171     struct stat finfo;
4172     
4173     //Exists?
4174     if (stat(native.c_str(), &finfo)<0)
4175         return false;
4178     //check the file mode
4179     if (!S_ISREG(finfo.st_mode))
4180         return false;
4182     return true;
4185 /**
4186  * Tests if the file exists and is a directory
4187  */ 
4188 bool MakeBase::isDirectory(const String &fileName)
4190     String native = getNativePath(fileName);
4191     struct stat finfo;
4192     
4193     //Exists?
4194     if (stat(native.c_str(), &finfo)<0)
4195         return false;
4198     //check the file mode
4199     if (!S_ISDIR(finfo.st_mode))
4200         return false;
4202     return true;
4207 /**
4208  * Tests is the modification of fileA is newer than fileB
4209  */ 
4210 bool MakeBase::isNewerThan(const String &fileA, const String &fileB)
4212     //trace("isNewerThan:'%s' , '%s'", fileA.c_str(), fileB.c_str());
4213     String nativeA = getNativePath(fileA);
4214     struct stat infoA;
4215     //IF source does not exist, NOT newer
4216     if (stat(nativeA.c_str(), &infoA)<0)
4217         {
4218         return false;
4219         }
4221     String nativeB = getNativePath(fileB);
4222     struct stat infoB;
4223     //IF dest does not exist, YES, newer
4224     if (stat(nativeB.c_str(), &infoB)<0)
4225         {
4226         return true;
4227         }
4229     //check the actual times
4230     if (infoA.st_mtime > infoB.st_mtime)
4231         {
4232         return true;
4233         }
4235     return false;
4239 //########################################################################
4240 //# P K G    C O N F I G
4241 //########################################################################
4243 /**
4244  *
4245  */
4246 class PkgConfig : public MakeBase
4249 public:
4251     /**
4252      *
4253      */
4254     PkgConfig()
4255         { init(); }
4257     /**
4258      *
4259      */
4260     PkgConfig(const String &namearg)
4261         { init(); name = namearg; }
4263     /**
4264      *
4265      */
4266     PkgConfig(const PkgConfig &other)
4267         { assign(other); }
4269     /**
4270      *
4271      */
4272     PkgConfig &operator=(const PkgConfig &other)
4273         { assign(other); return *this; }
4275     /**
4276      *
4277      */
4278     virtual ~PkgConfig()
4279         { }
4281     /**
4282      *
4283      */
4284     virtual String getName()
4285         { return name; }
4287     /**
4288      *
4289      */
4290     virtual String getDescription()
4291         { return description; }
4293     /**
4294      *
4295      */
4296     virtual String getCflags()
4297         { return cflags; }
4299     /**
4300      *
4301      */
4302     virtual String getLibs()
4303         { return libs; }
4305     /**
4306      *
4307      */
4308     virtual String getVersion()
4309         { return version; }
4311     /**
4312      *
4313      */
4314     virtual int getMajorVersion()
4315         { return majorVersion; }
4317     /**
4318      *
4319      */
4320     virtual int getMinorVersion()
4321         { return minorVersion; }
4323     /**
4324      *
4325      */
4326     virtual int getMicroVersion()
4327         { return microVersion; }
4329     /**
4330      *
4331      */
4332     virtual std::map<String, String> &getAttributes()
4333         { return attrs; }
4335     /**
4336      *
4337      */
4338     virtual std::vector<String> &getRequireList()
4339         { return requireList; }
4341     virtual bool readFile(const String &fileName);
4343 private:
4345     void init()
4346         {
4347         name         = "";
4348         description  = "";
4349         cflags       = "";
4350         libs         = "";
4351         requires     = "";
4352         version      = "";
4353         majorVersion = 0;
4354         minorVersion = 0;
4355         microVersion = 0;
4356         fileName     = "";
4357         attrs.clear();
4358         requireList.clear();
4359         }
4361     void assign(const PkgConfig &other)
4362         {
4363         name         = other.name;
4364         description  = other.description;
4365         cflags       = other.cflags;
4366         libs         = other.libs;
4367         requires     = other.requires;
4368         version      = other.version;
4369         majorVersion = other.majorVersion;
4370         minorVersion = other.minorVersion;
4371         microVersion = other.microVersion;
4372         fileName     = other.fileName;
4373         attrs        = other.attrs;
4374         requireList  = other.requireList;
4375         }
4379     int get(int pos);
4381     int skipwhite(int pos);
4383     int getword(int pos, String &ret);
4385     void parseRequires();
4387     void parseVersion();
4389     bool parse(const String &buf);
4391     void dumpAttrs();
4393     String name;
4395     String description;
4397     String cflags;
4399     String libs;
4401     String requires;
4403     String version;
4405     int majorVersion;
4407     int minorVersion;
4409     int microVersion;
4411     String fileName;
4413     std::map<String, String> attrs;
4415     std::vector<String> requireList;
4417     char *parsebuf;
4418     int parselen;
4419 };
4422 /**
4423  * Get a character from the buffer at pos.  If out of range,
4424  * return -1 for safety
4425  */
4426 int PkgConfig::get(int pos)
4428     if (pos>parselen)
4429         return -1;
4430     return parsebuf[pos];
4435 /**
4436  *  Skip over all whitespace characters beginning at pos.  Return
4437  *  the position of the first non-whitespace character.
4438  */
4439 int PkgConfig::skipwhite(int pos)
4441     while (pos < parselen)
4442         {
4443         int ch = get(pos);
4444         if (ch < 0)
4445             break;
4446         if (!isspace(ch))
4447             break;
4448         pos++;
4449         }
4450     return pos;
4454 /**
4455  *  Parse the buffer beginning at pos, for a word.  Fill
4456  *  'ret' with the result.  Return the position after the
4457  *  word.
4458  */
4459 int PkgConfig::getword(int pos, String &ret)
4461     while (pos < parselen)
4462         {
4463         int ch = get(pos);
4464         if (ch < 0)
4465             break;
4466         if (!isalnum(ch) && ch != '_' && ch != '-'&& ch != '.')
4467             break;
4468         ret.push_back((char)ch);
4469         pos++;
4470         }
4471     return pos;
4474 void PkgConfig::parseRequires()
4476     if (requires.size() == 0)
4477         return;
4478     parsebuf = (char *)requires.c_str();
4479     parselen = requires.size();
4480     int pos = 0;
4481     while (pos < parselen)
4482         {
4483         pos = skipwhite(pos);
4484         String val;
4485         int pos2 = getword(pos, val);
4486         if (pos2 == pos)
4487             break;
4488         pos = pos2;
4489         //trace("val %s", val.c_str());
4490         requireList.push_back(val);
4491         }
4494 static int getint(const String str)
4496     char *s = (char *)str.c_str();
4497     char *ends = NULL;
4498     long val = strtol(s, &ends, 10);
4499     if (ends == s)
4500         return 0L;
4501     else
4502         return val;
4505 void PkgConfig::parseVersion()
4507     if (version.size() == 0)
4508         return;
4509     String s1, s2, s3;
4510     unsigned int pos = 0;
4511     unsigned int pos2 = version.find('.', pos);
4512     if (pos2 == version.npos)
4513         {
4514         s1 = version;
4515         }
4516     else
4517         {
4518         s1 = version.substr(pos, pos2-pos);
4519         pos = pos2;
4520         pos++;
4521         if (pos < version.size())
4522             {
4523             pos2 = version.find('.', pos);
4524             if (pos2 == version.npos)
4525                 {
4526                 s2 = version.substr(pos, version.size()-pos);
4527                 }
4528             else
4529                 {
4530                 s2 = version.substr(pos, pos2-pos);
4531                 pos = pos2;
4532                 pos++;
4533                 if (pos < version.size())
4534                     s3 = version.substr(pos, pos2-pos);
4535                 }
4536             }
4537         }
4539     majorVersion = getint(s1);
4540     minorVersion = getint(s2);
4541     microVersion = getint(s3);
4542     //trace("version:%d.%d.%d", majorVersion,
4543     //          minorVersion, microVersion );
4547 bool PkgConfig::parse(const String &buf)
4549     init();
4551     parsebuf = (char *)buf.c_str();
4552     parselen = buf.size();
4553     int pos = 0;
4556     while (pos < parselen)
4557         {
4558         String attrName;
4559         pos = skipwhite(pos);
4560         int ch = get(pos);
4561         if (ch == '#')
4562             {
4563             //comment.  eat the rest of the line
4564             while (pos < parselen)
4565                 {
4566                 ch = get(pos);
4567                 if (ch == '\n' || ch < 0)
4568                     break;
4569                 pos++;
4570                 }
4571             continue;
4572             }
4573         pos = getword(pos, attrName);
4574         if (attrName.size() == 0)
4575             continue;
4576         pos = skipwhite(pos);
4577         ch = get(pos);
4578         if (ch != ':' && ch != '=')
4579             {
4580             error("expected ':' or '='");
4581             return false;
4582             }
4583         pos++;
4584         pos = skipwhite(pos);
4585         String attrVal;
4586         while (pos < parselen)
4587             {
4588             ch = get(pos);
4589             if (ch == '\n' || ch < 0)
4590                 break;
4591             else if (ch == '$' && get(pos+1) == '{')
4592                 {
4593                 //#  this is a ${substitution}
4594                 pos += 2;
4595                 String subName;
4596                 while (pos < parselen)
4597                     {
4598                     ch = get(pos);
4599                     if (ch < 0)
4600                         {
4601                         error("unterminated substitution");
4602                         return false;
4603                         }
4604                     else if (ch == '}')
4605                         break;
4606                     else
4607                         subName.push_back((char)ch);
4608                     pos++;
4609                     }
4610                 //trace("subName:%s", subName.c_str());
4611                 String subVal = attrs[subName];
4612                 //trace("subVal:%s", subVal.c_str());
4613                 attrVal.append(subVal);
4614                 }
4615             else
4616                 attrVal.push_back((char)ch);
4617             pos++;
4618             }
4620         attrVal = trim(attrVal);
4621         attrs[attrName] = attrVal;
4623         if (attrName == "Name")
4624             name = attrVal;
4625         else if (attrName == "Description")
4626             description = attrVal;
4627         else if (attrName == "Cflags")
4628             cflags = attrVal;
4629         else if (attrName == "Libs")
4630             libs = attrVal;
4631         else if (attrName == "Requires")
4632             requires = attrVal;
4633         else if (attrName == "Version")
4634             version = attrVal;
4636         //trace("name:'%s'  value:'%s'",
4637         //      attrName.c_str(), attrVal.c_str());
4638         }
4641     parseRequires();
4642     parseVersion();
4644     return true;
4647 void PkgConfig::dumpAttrs()
4649     //trace("### PkgConfig attributes for %s", fileName.c_str());
4650     std::map<String, String>::iterator iter;
4651     for (iter=attrs.begin() ; iter!=attrs.end() ; iter++)
4652         {
4653         trace("   %s = %s", iter->first.c_str(), iter->second.c_str());
4654         }
4658 bool PkgConfig::readFile(const String &fileNameArg)
4660     fileName = fileNameArg;
4662     FILE *f = fopen(fileName.c_str(), "r");
4663     if (!f)
4664         {
4665         error("cannot open file '%s' for reading", fileName.c_str());
4666         return false;
4667         }
4668     String buf;
4669     while (true)
4670         {
4671         int ch = fgetc(f);
4672         if (ch < 0)
4673             break;
4674         buf.push_back((char)ch);
4675         }
4676     fclose(f);
4678     //trace("####### File:\n%s", buf.c_str());
4679     if (!parse(buf))
4680         {
4681         return false;
4682         }
4684     dumpAttrs();
4686     return true;
4693 //########################################################################
4694 //# D E P T O O L
4695 //########################################################################
4699 /**
4700  *  Class which holds information for each file.
4701  */
4702 class FileRec
4704 public:
4706     typedef enum
4707         {
4708         UNKNOWN,
4709         CFILE,
4710         HFILE,
4711         OFILE
4712         } FileType;
4714     /**
4715      *  Constructor
4716      */
4717     FileRec()
4718         {init(); type = UNKNOWN;}
4720     /**
4721      *  Copy constructor
4722      */
4723     FileRec(const FileRec &other)
4724         {init(); assign(other);}
4725     /**
4726      *  Constructor
4727      */
4728     FileRec(int typeVal)
4729         {init(); type = typeVal;}
4730     /**
4731      *  Assignment operator
4732      */
4733     FileRec &operator=(const FileRec &other)
4734         {init(); assign(other); return *this;}
4737     /**
4738      *  Destructor
4739      */
4740     ~FileRec()
4741         {}
4743     /**
4744      *  Directory part of the file name
4745      */
4746     String path;
4748     /**
4749      *  Base name, sans directory and suffix
4750      */
4751     String baseName;
4753     /**
4754      *  File extension, such as cpp or h
4755      */
4756     String suffix;
4758     /**
4759      *  Type of file: CFILE, HFILE, OFILE
4760      */
4761     int type;
4763     /**
4764      * Used to list files ref'd by this one
4765      */
4766     std::map<String, FileRec *> files;
4769 private:
4771     void init()
4772         {
4773         }
4775     void assign(const FileRec &other)
4776         {
4777         type     = other.type;
4778         baseName = other.baseName;
4779         suffix   = other.suffix;
4780         files    = other.files;
4781         }
4783 };
4787 /**
4788  *  Simpler dependency record
4789  */
4790 class DepRec
4792 public:
4794     /**
4795      *  Constructor
4796      */
4797     DepRec()
4798         {init();}
4800     /**
4801      *  Copy constructor
4802      */
4803     DepRec(const DepRec &other)
4804         {init(); assign(other);}
4805     /**
4806      *  Constructor
4807      */
4808     DepRec(const String &fname)
4809         {init(); name = fname; }
4810     /**
4811      *  Assignment operator
4812      */
4813     DepRec &operator=(const DepRec &other)
4814         {init(); assign(other); return *this;}
4817     /**
4818      *  Destructor
4819      */
4820     ~DepRec()
4821         {}
4823     /**
4824      *  Directory part of the file name
4825      */
4826     String path;
4828     /**
4829      *  Base name, without the path and suffix
4830      */
4831     String name;
4833     /**
4834      *  Suffix of the source
4835      */
4836     String suffix;
4839     /**
4840      * Used to list files ref'd by this one
4841      */
4842     std::vector<String> files;
4845 private:
4847     void init()
4848         {
4849         }
4851     void assign(const DepRec &other)
4852         {
4853         path     = other.path;
4854         name     = other.name;
4855         suffix   = other.suffix;
4856         files    = other.files;
4857         }
4859 };
4862 class DepTool : public MakeBase
4864 public:
4866     /**
4867      *  Constructor
4868      */
4869     DepTool()
4870         {init();}
4872     /**
4873      *  Copy constructor
4874      */
4875     DepTool(const DepTool &other)
4876         {init(); assign(other);}
4878     /**
4879      *  Assignment operator
4880      */
4881     DepTool &operator=(const DepTool &other)
4882         {init(); assign(other); return *this;}
4885     /**
4886      *  Destructor
4887      */
4888     ~DepTool()
4889         {}
4892     /**
4893      *  Reset this section of code
4894      */
4895     virtual void init();
4896     
4897     /**
4898      *  Reset this section of code
4899      */
4900     virtual void assign(const DepTool &other)
4901         {
4902         }
4903     
4904     /**
4905      *  Sets the source directory which will be scanned
4906      */
4907     virtual void setSourceDirectory(const String &val)
4908         { sourceDir = val; }
4910     /**
4911      *  Returns the source directory which will be scanned
4912      */
4913     virtual String getSourceDirectory()
4914         { return sourceDir; }
4916     /**
4917      *  Sets the list of files within the directory to analyze
4918      */
4919     virtual void setFileList(const std::vector<String> &list)
4920         { fileList = list; }
4922     /**
4923      * Creates the list of all file names which will be
4924      * candidates for further processing.  Reads make.exclude
4925      * to see which files for directories to leave out.
4926      */
4927     virtual bool createFileList();
4930     /**
4931      *  Generates the forward dependency list
4932      */
4933     virtual bool generateDependencies();
4936     /**
4937      *  Generates the forward dependency list, saving the file
4938      */
4939     virtual bool generateDependencies(const String &);
4942     /**
4943      *  Load a dependency file
4944      */
4945     std::vector<DepRec> loadDepFile(const String &fileName);
4947     /**
4948      *  Load a dependency file, generating one if necessary
4949      */
4950     std::vector<DepRec> getDepFile(const String &fileName,
4951               bool forceRefresh);
4953     /**
4954      *  Save a dependency file
4955      */
4956     bool saveDepFile(const String &fileName);
4959 private:
4962     /**
4963      *
4964      */
4965     void parseName(const String &fullname,
4966                    String &path,
4967                    String &basename,
4968                    String &suffix);
4970     /**
4971      *
4972      */
4973     int get(int pos);
4975     /**
4976      *
4977      */
4978     int skipwhite(int pos);
4980     /**
4981      *
4982      */
4983     int getword(int pos, String &ret);
4985     /**
4986      *
4987      */
4988     bool sequ(int pos, char *key);
4990     /**
4991      *
4992      */
4993     bool addIncludeFile(FileRec *frec, const String &fname);
4995     /**
4996      *
4997      */
4998     bool scanFile(const String &fname, FileRec *frec);
5000     /**
5001      *
5002      */
5003     bool processDependency(FileRec *ofile,
5004                            FileRec *include,
5005                            int depth);
5007     /**
5008      *
5009      */
5010     String sourceDir;
5012     /**
5013      *
5014      */
5015     std::vector<String> fileList;
5017     /**
5018      *
5019      */
5020     std::vector<String> directories;
5022     /**
5023      * A list of all files which will be processed for
5024      * dependencies.  This is the only list that has the actual
5025      * records.  All other lists have pointers to these records.     
5026      */
5027     std::map<String, FileRec *> allFiles;
5029     /**
5030      * The list of .o files, and the
5031      * dependencies upon them.
5032      */
5033     std::map<String, FileRec *> depFiles;
5035     int depFileSize;
5036     char *depFileBuf;
5038     static const int readBufSize = 8192;
5039     char readBuf[8193];//byte larger
5041 };
5047 /**
5048  *  Clean up after processing.  Called by the destructor, but should
5049  *  also be called before the object is reused.
5050  */
5051 void DepTool::init()
5053     sourceDir = ".";
5055     fileList.clear();
5056     directories.clear();
5057     
5058     //clear refs
5059     depFiles.clear();
5060     //clear records
5061     std::map<String, FileRec *>::iterator iter;
5062     for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5063          delete iter->second;
5065     allFiles.clear(); 
5072 /**
5073  *  Parse a full path name into path, base name, and suffix
5074  */
5075 void DepTool::parseName(const String &fullname,
5076                         String &path,
5077                         String &basename,
5078                         String &suffix)
5080     if (fullname.size() < 2)
5081         return;
5083     unsigned int pos = fullname.find_last_of('/');
5084     if (pos != fullname.npos && pos<fullname.size()-1)
5085         {
5086         path = fullname.substr(0, pos);
5087         pos++;
5088         basename = fullname.substr(pos, fullname.size()-pos);
5089         }
5090     else
5091         {
5092         path = "";
5093         basename = fullname;
5094         }
5096     pos = basename.find_last_of('.');
5097     if (pos != basename.npos && pos<basename.size()-1)
5098         {
5099         suffix   = basename.substr(pos+1, basename.size()-pos-1);
5100         basename = basename.substr(0, pos);
5101         }
5103     //trace("parsename:%s %s %s", path.c_str(),
5104     //        basename.c_str(), suffix.c_str()); 
5109 /**
5110  *  Generate our internal file list.
5111  */
5112 bool DepTool::createFileList()
5115     for (unsigned int i=0 ; i<fileList.size() ; i++)
5116         {
5117         String fileName = fileList[i];
5118         //trace("## FileName:%s", fileName.c_str());
5119         String path;
5120         String basename;
5121         String sfx;
5122         parseName(fileName, path, basename, sfx);
5123         if (sfx == "cpp" || sfx == "c" || sfx == "cxx"   ||
5124             sfx == "cc" || sfx == "CC")
5125             {
5126             FileRec *fe         = new FileRec(FileRec::CFILE);
5127             fe->path            = path;
5128             fe->baseName        = basename;
5129             fe->suffix          = sfx;
5130             allFiles[fileName]  = fe;
5131             }
5132         else if (sfx == "h"   ||  sfx == "hh"  ||
5133                  sfx == "hpp" ||  sfx == "hxx")
5134             {
5135             FileRec *fe         = new FileRec(FileRec::HFILE);
5136             fe->path            = path;
5137             fe->baseName        = basename;
5138             fe->suffix          = sfx;
5139             allFiles[fileName]  = fe;
5140             }
5141         }
5143     if (!listDirectories(sourceDir, "", directories))
5144         return false;
5145         
5146     return true;
5153 /**
5154  * Get a character from the buffer at pos.  If out of range,
5155  * return -1 for safety
5156  */
5157 int DepTool::get(int pos)
5159     if (pos>depFileSize)
5160         return -1;
5161     return depFileBuf[pos];
5166 /**
5167  *  Skip over all whitespace characters beginning at pos.  Return
5168  *  the position of the first non-whitespace character.
5169  */
5170 int DepTool::skipwhite(int pos)
5172     while (pos < depFileSize)
5173         {
5174         int ch = get(pos);
5175         if (ch < 0)
5176             break;
5177         if (!isspace(ch))
5178             break;
5179         pos++;
5180         }
5181     return pos;
5185 /**
5186  *  Parse the buffer beginning at pos, for a word.  Fill
5187  *  'ret' with the result.  Return the position after the
5188  *  word.
5189  */
5190 int DepTool::getword(int pos, String &ret)
5192     while (pos < depFileSize)
5193         {
5194         int ch = get(pos);
5195         if (ch < 0)
5196             break;
5197         if (isspace(ch))
5198             break;
5199         ret.push_back((char)ch);
5200         pos++;
5201         }
5202     return pos;
5205 /**
5206  * Return whether the sequence of characters in the buffer
5207  * beginning at pos match the key,  for the length of the key
5208  */
5209 bool DepTool::sequ(int pos, char *key)
5211     while (*key)
5212         {
5213         if (*key != get(pos))
5214             return false;
5215         key++; pos++;
5216         }
5217     return true;
5222 /**
5223  *  Add an include file name to a file record.  If the name
5224  *  is not found in allFiles explicitly, try prepending include
5225  *  directory names to it and try again.
5226  */
5227 bool DepTool::addIncludeFile(FileRec *frec, const String &iname)
5230     std::map<String, FileRec *>::iterator iter =
5231            allFiles.find(iname);
5232     if (iter != allFiles.end()) //already exists
5233         {
5234          //h file in same dir
5235         FileRec *other = iter->second;
5236         //trace("local: '%s'", iname.c_str());
5237         frec->files[iname] = other;
5238         return true;
5239         }
5240     else 
5241         {
5242         //look in other dirs
5243         std::vector<String>::iterator diter;
5244         for (diter=directories.begin() ;
5245              diter!=directories.end() ; diter++)
5246             {
5247             String dfname = *diter;
5248             dfname.append("/");
5249             dfname.append(iname);
5250             iter = allFiles.find(dfname);
5251             if (iter != allFiles.end())
5252                 {
5253                 FileRec *other = iter->second;
5254                 //trace("other: '%s'", iname.c_str());
5255                 frec->files[dfname] = other;
5256                 return true;
5257                 }
5258             }
5259         }
5260     return true;
5265 /**
5266  *  Lightly parse a file to find the #include directives.  Do
5267  *  a bit of state machine stuff to make sure that the directive
5268  *  is valid.  (Like not in a comment).
5269  */
5270 bool DepTool::scanFile(const String &fname, FileRec *frec)
5272     String fileName;
5273     if (sourceDir.size() > 0)
5274         {
5275         fileName.append(sourceDir);
5276         fileName.append("/");
5277         }
5278     fileName.append(fname);
5279     String nativeName = getNativePath(fileName);
5280     FILE *f = fopen(nativeName.c_str(), "r");
5281     if (!f)
5282         {
5283         error("Could not open '%s' for reading", fname.c_str());
5284         return false;
5285         }
5286     String buf;
5287     while (!feof(f))
5288         {
5289         int len = fread(readBuf, 1, readBufSize, f);
5290         readBuf[len] = '\0';
5291         buf.append(readBuf);
5292         }
5293     fclose(f);
5295     depFileSize = buf.size();
5296     depFileBuf  = (char *)buf.c_str();
5297     int pos = 0;
5300     while (pos < depFileSize)
5301         {
5302         //trace("p:%c", get(pos));
5304         //# Block comment
5305         if (get(pos) == '/' && get(pos+1) == '*')
5306             {
5307             pos += 2;
5308             while (pos < depFileSize)
5309                 {
5310                 if (get(pos) == '*' && get(pos+1) == '/')
5311                     {
5312                     pos += 2;
5313                     break;
5314                     }
5315                 else
5316                     pos++;
5317                 }
5318             }
5319         //# Line comment
5320         else if (get(pos) == '/' && get(pos+1) == '/')
5321             {
5322             pos += 2;
5323             while (pos < depFileSize)
5324                 {
5325                 if (get(pos) == '\n')
5326                     {
5327                     pos++;
5328                     break;
5329                     }
5330                 else
5331                     pos++;
5332                 }
5333             }
5334         //# #include! yaay
5335         else if (sequ(pos, "#include"))
5336             {
5337             pos += 8;
5338             pos = skipwhite(pos);
5339             String iname;
5340             pos = getword(pos, iname);
5341             if (iname.size()>2)
5342                 {
5343                 iname = iname.substr(1, iname.size()-2);
5344                 addIncludeFile(frec, iname);
5345                 }
5346             }
5347         else
5348             {
5349             pos++;
5350             }
5351         }
5353     return true;
5358 /**
5359  *  Recursively check include lists to find all files in allFiles to which
5360  *  a given file is dependent.
5361  */
5362 bool DepTool::processDependency(FileRec *ofile,
5363                              FileRec *include,
5364                              int depth)
5366     std::map<String, FileRec *>::iterator iter;
5367     for (iter=include->files.begin() ; iter!=include->files.end() ; iter++)
5368         {
5369         String fname  = iter->first;
5370         if (ofile->files.find(fname) != ofile->files.end())
5371             {
5372             //trace("file '%s' already seen", fname.c_str());
5373             continue;
5374             }
5375         FileRec *child  = iter->second;
5376         ofile->files[fname] = child;
5377       
5378         processDependency(ofile, child, depth+1);
5379         }
5382     return true;
5389 /**
5390  *  Generate the file dependency list.
5391  */
5392 bool DepTool::generateDependencies()
5394     std::map<String, FileRec *>::iterator iter;
5395     //# First pass.  Scan for all includes
5396     for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5397         {
5398         FileRec *frec = iter->second;
5399         if (!scanFile(iter->first, frec))
5400             {
5401             //quit?
5402             }
5403         }
5405     //# Second pass.  Scan for all includes
5406     for (iter=allFiles.begin() ; iter!=allFiles.end() ; iter++)
5407         {
5408         FileRec *include = iter->second;
5409         if (include->type == FileRec::CFILE)
5410             {
5411             String cFileName = iter->first;
5412             FileRec *ofile      = new FileRec(FileRec::OFILE);
5413             ofile->path         = include->path;
5414             ofile->baseName     = include->baseName;
5415             ofile->suffix       = include->suffix;
5416             String fname     = include->path;
5417             if (fname.size()>0)
5418                 fname.append("/");
5419             fname.append(include->baseName);
5420             fname.append(".o");
5421             depFiles[fname]    = ofile;
5422             //add the .c file first?   no, don't
5423             //ofile->files[cFileName] = include;
5424             
5425             //trace("ofile:%s", fname.c_str());
5427             processDependency(ofile, include, 0);
5428             }
5429         }
5431       
5432     return true;
5437 /**
5438  *  High-level call to generate deps and optionally save them
5439  */
5440 bool DepTool::generateDependencies(const String &fileName)
5442     if (!createFileList())
5443         return false;
5444     if (!generateDependencies())
5445         return false;
5446     if (!saveDepFile(fileName))
5447         return false;
5448     return true;
5452 /**
5453  *   This saves the dependency cache.
5454  */
5455 bool DepTool::saveDepFile(const String &fileName)
5457     time_t tim;
5458     time(&tim);
5460     FILE *f = fopen(fileName.c_str(), "w");
5461     if (!f)
5462         {
5463         trace("cannot open '%s' for writing", fileName.c_str());
5464         }
5465     fprintf(f, "<?xml version='1.0'?>\n");
5466     fprintf(f, "<!--\n");
5467     fprintf(f, "########################################################\n");
5468     fprintf(f, "## File: build.dep\n");
5469     fprintf(f, "## Generated by BuildTool at :%s", ctime(&tim));
5470     fprintf(f, "########################################################\n");
5471     fprintf(f, "-->\n");
5473     fprintf(f, "<dependencies source='%s'>\n\n", sourceDir.c_str());
5474     std::map<String, FileRec *>::iterator iter;
5475     for (iter=depFiles.begin() ; iter!=depFiles.end() ; iter++)
5476         {
5477         FileRec *frec = iter->second;
5478         if (frec->type == FileRec::OFILE)
5479             {
5480             fprintf(f, "<object path='%s' name='%s' suffix='%s'>\n",
5481                  frec->path.c_str(), frec->baseName.c_str(), frec->suffix.c_str());
5482             std::map<String, FileRec *>::iterator citer;
5483             for (citer=frec->files.begin() ; citer!=frec->files.end() ; citer++)
5484                 {
5485                 String cfname = citer->first;
5486                 fprintf(f, "    <dep name='%s'/>\n", cfname.c_str());
5487                 }
5488             fprintf(f, "</object>\n\n");
5489             }
5490         }
5492     fprintf(f, "</dependencies>\n");
5493     fprintf(f, "\n");
5494     fprintf(f, "<!--\n");
5495     fprintf(f, "########################################################\n");
5496     fprintf(f, "## E N D\n");
5497     fprintf(f, "########################################################\n");
5498     fprintf(f, "-->\n");
5500     fclose(f);
5502     return true;
5508 /**
5509  *   This loads the dependency cache.
5510  */
5511 std::vector<DepRec> DepTool::loadDepFile(const String &depFile)
5513     std::vector<DepRec> result;
5514     
5515     Parser parser;
5516     Element *root = parser.parseFile(depFile.c_str());
5517     if (!root)
5518         {
5519         //error("Could not open %s for reading", depFile.c_str());
5520         return result;
5521         }
5523     if (root->getChildren().size()==0 ||
5524         root->getChildren()[0]->getName()!="dependencies")
5525         {
5526         error("Main xml element should be <dependencies>");
5527         delete root;
5528         return result;
5529         }
5531     //########## Start parsing
5532     Element *depList = root->getChildren()[0];
5534     std::vector<Element *> objects = depList->getChildren();
5535     for (unsigned int i=0 ; i<objects.size() ; i++)
5536         {
5537         Element *objectElem = objects[i];
5538         String tagName = objectElem->getName();
5539         if (tagName == "object")
5540             {
5541             String objName   = objectElem->getAttribute("name");
5542              //trace("object:%s", objName.c_str());
5543             DepRec depObject(objName);
5544             depObject.path   = objectElem->getAttribute("path");
5545             depObject.suffix = objectElem->getAttribute("suffix");
5546             //########## DESCRIPTION
5547             std::vector<Element *> depElems = objectElem->getChildren();
5548             for (unsigned int i=0 ; i<depElems.size() ; i++)
5549                 {
5550                 Element *depElem = depElems[i];
5551                 tagName = depElem->getName();
5552                 if (tagName == "dep")
5553                     {
5554                     String depName = depElem->getAttribute("name");
5555                     //trace("    dep:%s", depName.c_str());
5556                     depObject.files.push_back(depName);
5557                     }
5558                 }
5559             //Insert into the result list, in a sorted manner
5560             bool inserted = false;
5561             std::vector<DepRec>::iterator iter;
5562             for (iter = result.begin() ; iter != result.end() ; iter++)
5563                 {
5564                 String vpath = iter->path;
5565                 vpath.append("/");
5566                 vpath.append(iter->name);
5567                 String opath = depObject.path;
5568                 opath.append("/");
5569                 opath.append(depObject.name);
5570                 if (vpath > opath)
5571                     {
5572                     inserted = true;
5573                     iter = result.insert(iter, depObject);
5574                     break;
5575                     }
5576                 }
5577             if (!inserted)
5578                 result.push_back(depObject);
5579             }
5580         }
5582     delete root;
5584     return result;
5588 /**
5589  *   This loads the dependency cache.
5590  */
5591 std::vector<DepRec> DepTool::getDepFile(const String &depFile,
5592                    bool forceRefresh)
5594     std::vector<DepRec> result;
5595     if (forceRefresh)
5596         {
5597         generateDependencies(depFile);
5598         result = loadDepFile(depFile);
5599         }
5600     else
5601         {
5602         //try once
5603         result = loadDepFile(depFile);
5604         if (result.size() == 0)
5605             {
5606             //fail? try again
5607             generateDependencies(depFile);
5608             result = loadDepFile(depFile);
5609             }
5610         }
5611     return result;
5617 //########################################################################
5618 //# T A S K
5619 //########################################################################
5620 //forward decl
5621 class Target;
5622 class Make;
5624 /**
5625  *
5626  */
5627 class Task : public MakeBase
5630 public:
5632     typedef enum
5633         {
5634         TASK_NONE,
5635         TASK_CC,
5636         TASK_COPY,
5637         TASK_DELETE,
5638         TASK_JAR,
5639         TASK_JAVAC,
5640         TASK_LINK,
5641         TASK_MAKEFILE,
5642         TASK_MKDIR,
5643         TASK_MSGFMT,
5644         TASK_RANLIB,
5645         TASK_RC,
5646         TASK_SHAREDLIB,
5647         TASK_STATICLIB,
5648         TASK_STRIP,
5649         TASK_TOUCH,
5650         TASK_TSTAMP
5651         } TaskType;
5652         
5654     /**
5655      *
5656      */
5657     Task(MakeBase &par) : parent(par)
5658         { init(); }
5660     /**
5661      *
5662      */
5663     Task(const Task &other) : parent(other.parent)
5664         { init(); assign(other); }
5666     /**
5667      *
5668      */
5669     Task &operator=(const Task &other)
5670         { assign(other); return *this; }
5672     /**
5673      *
5674      */
5675     virtual ~Task()
5676         { }
5679     /**
5680      *
5681      */
5682     virtual MakeBase &getParent()
5683         { return parent; }
5685      /**
5686      *
5687      */
5688     virtual int  getType()
5689         { return type; }
5691     /**
5692      *
5693      */
5694     virtual void setType(int val)
5695         { type = val; }
5697     /**
5698      *
5699      */
5700     virtual String getName()
5701         { return name; }
5703     /**
5704      *
5705      */
5706     virtual bool execute()
5707         { return true; }
5709     /**
5710      *
5711      */
5712     virtual bool parse(Element *elem)
5713         { return true; }
5715     /**
5716      *
5717      */
5718     Task *createTask(Element *elem);
5721 protected:
5723     void init()
5724         {
5725         type = TASK_NONE;
5726         name = "none";
5727         }
5729     void assign(const Task &other)
5730         {
5731         type = other.type;
5732         name = other.name;
5733         }
5734         
5735     String getAttribute(Element *elem, const String &attrName)
5736         {
5737         String str;
5738         return str;
5739         }
5741     MakeBase &parent;
5743     int type;
5745     String name;
5746 };
5750 /**
5751  * This task runs the C/C++ compiler.  The compiler is invoked
5752  * for all .c or .cpp files which are newer than their correcsponding
5753  * .o files.  
5754  */
5755 class TaskCC : public Task
5757 public:
5759     TaskCC(MakeBase &par) : Task(par)
5760         {
5761         type = TASK_CC; name = "cc";
5762         ccCommand   = "gcc";
5763         cxxCommand  = "g++";
5764         source      = ".";
5765         dest        = ".";
5766         flags       = "";
5767         defines     = "";
5768         includes    = "";
5769         fileSet.clear();
5770         }
5772     virtual ~TaskCC()
5773         {}
5775     virtual bool needsCompiling(const DepRec &depRec,
5776               const String &src, const String &dest)
5777         {
5778         return false;
5779         }
5781     virtual bool execute()
5782         {
5783         if (!listFiles(parent, fileSet))
5784             return false;
5786         bool refreshCache = false;
5787         String fullName = parent.resolve("build.dep");
5788         if (isNewerThan(parent.getURI().getPath(), fullName))
5789             {
5790             status("          : regenerating C/C++ dependency cache");
5791             refreshCache = true;
5792             }
5794         DepTool depTool;
5795         depTool.setSourceDirectory(source);
5796         depTool.setFileList(fileSet.getFiles());
5797         std::vector<DepRec> deps =
5798              depTool.getDepFile("build.dep", refreshCache);
5799         
5800         String incs;
5801         incs.append("-I");
5802         incs.append(parent.resolve("."));
5803         incs.append(" ");
5804         if (includes.size()>0)
5805             {
5806             incs.append(includes);
5807             incs.append(" ");
5808             }
5809         std::set<String> paths;
5810         std::vector<DepRec>::iterator viter;
5811         for (viter=deps.begin() ; viter!=deps.end() ; viter++)
5812             {
5813             DepRec dep = *viter;
5814             if (dep.path.size()>0)
5815                 paths.insert(dep.path);
5816             }
5817         if (source.size()>0)
5818             {
5819             incs.append(" -I");
5820             incs.append(parent.resolve(source));
5821             incs.append(" ");
5822             }
5823         std::set<String>::iterator setIter;
5824         for (setIter=paths.begin() ; setIter!=paths.end() ; setIter++)
5825             {
5826             incs.append(" -I");
5827             String dname;
5828             if (source.size()>0)
5829                 {
5830                 dname.append(source);
5831                 dname.append("/");
5832                 }
5833             dname.append(*setIter);
5834             incs.append(parent.resolve(dname));
5835             }
5836         std::vector<String> cfiles;
5837         for (viter=deps.begin() ; viter!=deps.end() ; viter++)
5838             {
5839             DepRec dep = *viter;
5841             //## Select command
5842             String sfx = dep.suffix;
5843             String command = ccCommand;
5844             if (sfx == "cpp" || sfx == "c++" || sfx == "cc"
5845                  || sfx == "CC")
5846                 command = cxxCommand;
5847  
5848             //## Make paths
5849             String destPath = dest;
5850             String srcPath  = source;
5851             if (dep.path.size()>0)
5852                 {
5853                 destPath.append("/");
5854                 destPath.append(dep.path);
5855                 srcPath.append("/");
5856                 srcPath.append(dep.path);
5857                 }
5858             //## Make sure destination directory exists
5859             if (!createDirectory(destPath))
5860                 return false;
5861                 
5862             //## Check whether it needs to be done
5863             String destName;
5864             if (destPath.size()>0)
5865                 {
5866                 destName.append(destPath);
5867                 destName.append("/");
5868                 }
5869             destName.append(dep.name);
5870             destName.append(".o");
5871             String destFullName = parent.resolve(destName);
5872             String srcName;
5873             if (srcPath.size()>0)
5874                 {
5875                 srcName.append(srcPath);
5876                 srcName.append("/");
5877                 }
5878             srcName.append(dep.name);
5879             srcName.append(".");
5880             srcName.append(dep.suffix);
5881             String srcFullName = parent.resolve(srcName);
5882             bool compileMe = false;
5883             if (isNewerThan(srcFullName, destFullName))
5884                 {
5885                 status("          : compile of %s required by %s",
5886                         destFullName.c_str(), srcFullName.c_str());
5887                 compileMe = true;
5888                 }
5889             else
5890                 {
5891                 for (unsigned int i=0 ; i<dep.files.size() ; i++)
5892                     {
5893                     String depName;
5894                     if (srcPath.size()>0)
5895                         {
5896                         depName.append(srcPath);
5897                         depName.append("/");
5898                         }
5899                     depName.append(dep.files[i]);
5900                     String depFullName = parent.resolve(depName);
5901                     if (isNewerThan(depFullName, destFullName))
5902                         {
5903                         status("          : compile of %s required by %s",
5904                                 destFullName.c_str(), depFullName.c_str());
5905                         compileMe = true;
5906                         break;
5907                         }
5908                     }
5909                 }
5910             if (!compileMe)
5911                 {
5912                 continue;
5913                 }
5915             //## Assemble the command
5916             String cmd = command;
5917             cmd.append(" -c ");
5918             cmd.append(flags);
5919             cmd.append(" ");
5920             cmd.append(defines);
5921             cmd.append(" ");
5922             cmd.append(incs);
5923             cmd.append(" ");
5924             cmd.append(srcFullName);
5925             cmd.append(" -o ");
5926             cmd.append(destFullName);
5928             //## Execute the command
5930             String outString, errString;
5931             if (!executeCommand(cmd.c_str(), "", outString, errString))
5932                 {
5933                 error("problem compiling: %s", errString.c_str());
5934                 return false;
5935                 }
5936             }
5937         
5938         return true;
5939         }
5941     virtual bool parse(Element *elem)
5942         {
5943         String s;
5944         if (!parent.getAttribute(elem, "command", s))
5945             return false;
5946         if (s.size()>0) { ccCommand = s; cxxCommand = s; }
5947         if (!parent.getAttribute(elem, "cc", s))
5948             return false;
5949         if (s.size()>0) ccCommand = s;
5950         if (!parent.getAttribute(elem, "cxx", s))
5951             return false;
5952         if (s.size()>0) cxxCommand = s;
5953         if (!parent.getAttribute(elem, "destdir", s))
5954             return false;
5955         if (s.size()>0) dest = s;
5957         std::vector<Element *> children = elem->getChildren();
5958         for (unsigned int i=0 ; i<children.size() ; i++)
5959             {
5960             Element *child = children[i];
5961             String tagName = child->getName();
5962             if (tagName == "flags")
5963                 {
5964                 if (!parent.getValue(child, flags))
5965                     return false;
5966                 flags = strip(flags);
5967                 }
5968             else if (tagName == "includes")
5969                 {
5970                 if (!parent.getValue(child, includes))
5971                     return false;
5972                 includes = strip(includes);
5973                 }
5974             else if (tagName == "defines")
5975                 {
5976                 if (!parent.getValue(child, defines))
5977                     return false;
5978                 defines = strip(defines);
5979                 }
5980             else if (tagName == "fileset")
5981                 {
5982                 if (!parseFileSet(child, parent, fileSet))
5983                     return false;
5984                 source = fileSet.getDirectory();
5985                 }
5986             }
5988         return true;
5989         }
5990         
5991 protected:
5993     String ccCommand;
5994     String cxxCommand;
5995     String source;
5996     String dest;
5997     String flags;
5998     String defines;
5999     String includes;
6000     FileSet fileSet;
6001     
6002 };
6006 /**
6007  *
6008  */
6009 class TaskCopy : public Task
6011 public:
6013     typedef enum
6014         {
6015         CP_NONE,
6016         CP_TOFILE,
6017         CP_TODIR
6018         } CopyType;
6020     TaskCopy(MakeBase &par) : Task(par)
6021         {
6022         type = TASK_COPY; name = "copy";
6023         cptype = CP_NONE;
6024         verbose = false;
6025         haveFileSet = false;
6026         }
6028     virtual ~TaskCopy()
6029         {}
6031     virtual bool execute()
6032         {
6033         switch (cptype)
6034            {
6035            case CP_TOFILE:
6036                {
6037                if (fileName.size()>0)
6038                    {
6039                    status("          : %s to %s",
6040                         fileName.c_str(), toFileName.c_str());
6041                    String fullSource = parent.resolve(fileName);
6042                    String fullDest = parent.resolve(toFileName);
6043                    //trace("copy %s to file %s", fullSource.c_str(),
6044                    //                       fullDest.c_str());
6045                    if (!isRegularFile(fullSource))
6046                        {
6047                        error("copy : file %s does not exist", fullSource.c_str());
6048                        return false;
6049                        }
6050                    if (!isNewerThan(fullSource, fullDest))
6051                        {
6052                        return true;
6053                        }
6054                    if (!copyFile(fullSource, fullDest))
6055                        return false;
6056                    status("          : 1 file copied");
6057                    }
6058                return true;
6059                }
6060            case CP_TODIR:
6061                {
6062                if (haveFileSet)
6063                    {
6064                    if (!listFiles(parent, fileSet))
6065                        return false;
6066                    String fileSetDir = fileSet.getDirectory();
6068                    status("          : %s to %s",
6069                        fileSetDir.c_str(), toDirName.c_str());
6071                    int nrFiles = 0;
6072                    for (unsigned int i=0 ; i<fileSet.size() ; i++)
6073                        {
6074                        String fileName = fileSet[i];
6076                        String sourcePath;
6077                        if (fileSetDir.size()>0)
6078                            {
6079                            sourcePath.append(fileSetDir);
6080                            sourcePath.append("/");
6081                            }
6082                        sourcePath.append(fileName);
6083                        String fullSource = parent.resolve(sourcePath);
6084                        
6085                        //Get the immediate parent directory's base name
6086                        String baseFileSetDir = fileSetDir;
6087                        unsigned int pos = baseFileSetDir.find_last_of('/');
6088                        if (pos!=baseFileSetDir.npos &&
6089                                   pos < baseFileSetDir.size()-1)
6090                            baseFileSetDir =
6091                               baseFileSetDir.substr(pos+1,
6092                                    baseFileSetDir.size());
6093                        //Now make the new path
6094                        String destPath;
6095                        if (toDirName.size()>0)
6096                            {
6097                            destPath.append(toDirName);
6098                            destPath.append("/");
6099                            }
6100                        if (baseFileSetDir.size()>0)
6101                            {
6102                            destPath.append(baseFileSetDir);
6103                            destPath.append("/");
6104                            }
6105                        destPath.append(fileName);
6106                        String fullDest = parent.resolve(destPath);
6107                        //trace("fileName:%s", fileName.c_str());
6108                        //trace("copy %s to new dir : %s", fullSource.c_str(),
6109                        //                   fullDest.c_str());
6110                        if (!isNewerThan(fullSource, fullDest))
6111                            {
6112                            //trace("copy skipping %s", fullSource.c_str());
6113                            continue;
6114                            }
6115                        if (!copyFile(fullSource, fullDest))
6116                            return false;
6117                        nrFiles++;
6118                        }
6119                    status("          : %d file(s) copied", nrFiles);
6120                    }
6121                else //file source
6122                    {
6123                    //For file->dir we want only the basename of
6124                    //the source appended to the dest dir
6125                    status("          : %s to %s", 
6126                        fileName.c_str(), toDirName.c_str());
6127                    String baseName = fileName;
6128                    unsigned int pos = baseName.find_last_of('/');
6129                    if (pos!=baseName.npos && pos<baseName.size()-1)
6130                        baseName = baseName.substr(pos+1, baseName.size());
6131                    String fullSource = parent.resolve(fileName);
6132                    String destPath;
6133                    if (toDirName.size()>0)
6134                        {
6135                        destPath.append(toDirName);
6136                        destPath.append("/");
6137                        }
6138                    destPath.append(baseName);
6139                    String fullDest = parent.resolve(destPath);
6140                    //trace("copy %s to new dir : %s", fullSource.c_str(),
6141                    //                       fullDest.c_str());
6142                    if (!isRegularFile(fullSource))
6143                        {
6144                        error("copy : file %s does not exist", fullSource.c_str());
6145                        return false;
6146                        }
6147                    if (!isNewerThan(fullSource, fullDest))
6148                        {
6149                        return true;
6150                        }
6151                    if (!copyFile(fullSource, fullDest))
6152                        return false;
6153                    status("          : 1 file copied");
6154                    }
6155                return true;
6156                }
6157            }
6158         return true;
6159         }
6162     virtual bool parse(Element *elem)
6163         {
6164         if (!parent.getAttribute(elem, "file", fileName))
6165             return false;
6166         if (!parent.getAttribute(elem, "tofile", toFileName))
6167             return false;
6168         if (toFileName.size() > 0)
6169             cptype = CP_TOFILE;
6170         if (!parent.getAttribute(elem, "todir", toDirName))
6171             return false;
6172         if (toDirName.size() > 0)
6173             cptype = CP_TODIR;
6174         String ret;
6175         if (!parent.getAttribute(elem, "verbose", ret))
6176             return false;
6177         if (ret.size()>0 && !getBool(ret, verbose))
6178             return false;
6179             
6180         haveFileSet = false;
6181         
6182         std::vector<Element *> children = elem->getChildren();
6183         for (unsigned int i=0 ; i<children.size() ; i++)
6184             {
6185             Element *child = children[i];
6186             String tagName = child->getName();
6187             if (tagName == "fileset")
6188                 {
6189                 if (!parseFileSet(child, parent, fileSet))
6190                     {
6191                     error("problem getting fileset");
6192                     return false;
6193                     }
6194                 haveFileSet = true;
6195                 }
6196             }
6198         //Perform validity checks
6199         if (fileName.size()>0 && fileSet.size()>0)
6200             {
6201             error("<copy> can only have one of : file= and <fileset>");
6202             return false;
6203             }
6204         if (toFileName.size()>0 && toDirName.size()>0)
6205             {
6206             error("<copy> can only have one of : tofile= or todir=");
6207             return false;
6208             }
6209         if (haveFileSet && toDirName.size()==0)
6210             {
6211             error("a <copy> task with a <fileset> must have : todir=");
6212             return false;
6213             }
6214         if (cptype == CP_TOFILE && fileName.size()==0)
6215             {
6216             error("<copy> tofile= must be associated with : file=");
6217             return false;
6218             }
6219         if (cptype == CP_TODIR && fileName.size()==0 && !haveFileSet)
6220             {
6221             error("<copy> todir= must be associated with : file= or <fileset>");
6222             return false;
6223             }
6225         return true;
6226         }
6227         
6228 private:
6230     int cptype;
6231     String fileName;
6232     FileSet fileSet;
6233     String toFileName;
6234     String toDirName;
6235     bool verbose;
6236     bool haveFileSet;
6237 };
6240 /**
6241  *
6242  */
6243 class TaskDelete : public Task
6245 public:
6247     typedef enum
6248         {
6249         DEL_FILE,
6250         DEL_DIR,
6251         DEL_FILESET
6252         } DeleteType;
6254     TaskDelete(MakeBase &par) : Task(par)
6255         { 
6256           type        = TASK_DELETE;
6257           name        = "delete";
6258           delType     = DEL_FILE;
6259           verbose     = false;
6260           quiet       = false;
6261           failOnError = true;
6262         }
6264     virtual ~TaskDelete()
6265         {}
6267     virtual bool execute()
6268         {
6269         struct stat finfo;
6270         switch (delType)
6271             {
6272             case DEL_FILE:
6273                 {
6274                 status("          : %s", fileName.c_str());
6275                 String fullName = parent.resolve(fileName);
6276                 char *fname = (char *)fullName.c_str();
6277                 //does not exist
6278                 if (stat(fname, &finfo)<0)
6279                     return true;
6280                 //exists but is not a regular file
6281                 if (!S_ISREG(finfo.st_mode))
6282                     {
6283                     error("<delete> failed. '%s' exists and is not a regular file",
6284                           fname);
6285                     return false;
6286                     }
6287                 if (remove(fname)<0)
6288                     {
6289                     error("<delete> failed: %s", strerror(errno));
6290                     return false;
6291                     }
6292                 return true;
6293                 }
6294             case DEL_DIR:
6295                 {
6296                 status("          : %s", dirName.c_str());
6297                 String fullDir = parent.resolve(dirName);
6298                 if (!removeDirectory(fullDir))
6299                     return false;
6300                 return true;
6301                 }
6302             }
6303         return true;
6304         }
6306     virtual bool parse(Element *elem)
6307         {
6308         if (!parent.getAttribute(elem, "file", fileName))
6309             return false;
6310         if (fileName.size() > 0)
6311             delType = DEL_FILE;
6312         if (!parent.getAttribute(elem, "dir", dirName))
6313             return false;
6314         if (dirName.size() > 0)
6315             delType = DEL_DIR;
6316         if (fileName.size()>0 && dirName.size()>0)
6317             {
6318             error("<delete> can only have one attribute of file= or dir=");
6319             return false;
6320             }
6321         String ret;
6322         if (!parent.getAttribute(elem, "verbose", ret))
6323             return false;
6324         if (ret.size()>0 && !getBool(ret, verbose))
6325             return false;
6326         if (!parent.getAttribute(elem, "quiet", ret))
6327             return false;
6328         if (ret.size()>0 && !getBool(ret, quiet))
6329             return false;
6330         if (!parent.getAttribute(elem, "failonerror", ret))
6331             return false;
6332         if (ret.size()>0 && !getBool(ret, failOnError))
6333             return false;
6334         return true;
6335         }
6337 private:
6339     int delType;
6340     String dirName;
6341     String fileName;
6342     bool verbose;
6343     bool quiet;
6344     bool failOnError;
6345 };
6348 /**
6349  *
6350  */
6351 class TaskJar : public Task
6353 public:
6355     TaskJar(MakeBase &par) : Task(par)
6356         { type = TASK_JAR; name = "jar"; }
6358     virtual ~TaskJar()
6359         {}
6361     virtual bool execute()
6362         {
6363         return true;
6364         }
6366     virtual bool parse(Element *elem)
6367         {
6368         return true;
6369         }
6370 };
6373 /**
6374  *
6375  */
6376 class TaskJavac : public Task
6378 public:
6380     TaskJavac(MakeBase &par) : Task(par)
6381         { type = TASK_JAVAC; name = "javac"; }
6383     virtual ~TaskJavac()
6384         {}
6386     virtual bool execute()
6387         {
6388         return true;
6389         }
6391     virtual bool parse(Element *elem)
6392         {
6393         return true;
6394         }
6395 };
6398 /**
6399  *
6400  */
6401 class TaskLink : public Task
6403 public:
6405     TaskLink(MakeBase &par) : Task(par)
6406         {
6407         type = TASK_LINK; name = "link";
6408         command = "g++";
6409         doStrip = false;
6410                 stripCommand = "strip";
6411                 objcopyCommand = "objcopy";
6412         }
6414     virtual ~TaskLink()
6415         {}
6417     virtual bool execute()
6418         {
6419         if (!listFiles(parent, fileSet))
6420             return false;
6421         String fileSetDir = fileSet.getDirectory();
6422         //trace("%d files in %s", fileSet.size(), fileSetDir.c_str());
6423         bool doit = false;
6424         String fullTarget = parent.resolve(fileName);
6425         String cmd = command;
6426         cmd.append(" -o ");
6427         cmd.append(fullTarget);
6428         cmd.append(" ");
6429         cmd.append(flags);
6430         for (unsigned int i=0 ; i<fileSet.size() ; i++)
6431             {
6432             cmd.append(" ");
6433             String obj;
6434             if (fileSetDir.size()>0)
6435                 {
6436                 obj.append(fileSetDir);
6437                 obj.append("/");
6438                 }
6439             obj.append(fileSet[i]);
6440             String fullObj = parent.resolve(obj);
6441             cmd.append(fullObj);
6442             //trace("link: tgt:%s obj:%s", fullTarget.c_str(),
6443             //          fullObj.c_str());
6444             if (isNewerThan(fullObj, fullTarget))
6445                 doit = true;
6446             }
6447         cmd.append(" ");
6448         cmd.append(libs);
6449         if (!doit)
6450             {
6451             //trace("link not needed");
6452             return true;
6453             }
6454         //trace("LINK cmd:%s", cmd.c_str());
6457         String outbuf, errbuf;
6458         if (!executeCommand(cmd.c_str(), "", outbuf, errbuf))
6459             {
6460             error("LINK problem: %s", errbuf.c_str());
6461             return false;
6462             }
6464         if (symFileName.size()>0)
6465             {
6466             String symFullName = parent.resolve(symFileName);
6467             cmd = objcopyCommand;
6468             cmd.append(" --only-keep-debug ");
6469             cmd.append(getNativePath(fullTarget));
6470             cmd.append(" ");
6471             cmd.append(getNativePath(symFullName));
6472             if (!executeCommand(cmd, "", outbuf, errbuf))
6473                 {
6474                 error("<strip> symbol file failed : %s", errbuf.c_str());
6475                 return false;
6476                 }
6477             }
6478             
6479         if (doStrip)
6480             {
6481             cmd = stripCommand;
6482             cmd.append(" ");
6483             cmd.append(getNativePath(fullTarget));
6484             if (!executeCommand(cmd, "", outbuf, errbuf))
6485                {
6486                error("<strip> failed : %s", errbuf.c_str());
6487                return false;
6488                }
6489             }
6491         return true;
6492         }
6494     virtual bool parse(Element *elem)
6495         {
6496         String s;
6497         if (!parent.getAttribute(elem, "command", s))
6498             return false;
6499         if (s.size()>0)
6500             command = s;
6501         if (!parent.getAttribute(elem, "objcopycommand", s))
6502             return false;
6503         if (s.size()>0)
6504             objcopyCommand = s;
6505         if (!parent.getAttribute(elem, "stripcommand", s))
6506             return false;
6507         if (s.size()>0)
6508             stripCommand = s;
6509         if (!parent.getAttribute(elem, "out", fileName))
6510             return false;
6511         if (!parent.getAttribute(elem, "strip", s))
6512             return false;
6513         if (s.size()>0 && !getBool(s, doStrip))
6514             return false;
6515         if (!parent.getAttribute(elem, "symfile", symFileName))
6516             return false;
6517             
6518         std::vector<Element *> children = elem->getChildren();
6519         for (unsigned int i=0 ; i<children.size() ; i++)
6520             {
6521             Element *child = children[i];
6522             String tagName = child->getName();
6523             if (tagName == "fileset")
6524                 {
6525                 if (!parseFileSet(child, parent, fileSet))
6526                     return false;
6527                 }
6528             else if (tagName == "flags")
6529                 {
6530                 if (!parent.getValue(child, flags))
6531                     return false;
6532                 flags = strip(flags);
6533                 }
6534             else if (tagName == "libs")
6535                 {
6536                 if (!parent.getValue(child, libs))
6537                     return false;
6538                 libs = strip(libs);
6539                 }
6540             }
6541         return true;
6542         }
6544 private:
6546     String  command;
6547     String  fileName;
6548     String  flags;
6549     String  libs;
6550     FileSet fileSet;
6551     bool    doStrip;
6552     String  symFileName;
6553     String  stripCommand;
6554     String  objcopyCommand;
6556 };
6560 /**
6561  * Create a named directory
6562  */
6563 class TaskMakeFile : public Task
6565 public:
6567     TaskMakeFile(MakeBase &par) : Task(par)
6568         { type = TASK_MAKEFILE; name = "makefile"; }
6570     virtual ~TaskMakeFile()
6571         {}
6573     virtual bool execute()
6574         {
6575         status("          : %s", fileName.c_str());
6576         String fullName = parent.resolve(fileName);
6577         if (!isNewerThan(parent.getURI().getPath(), fullName))
6578             {
6579             //trace("skipped <makefile>");
6580             return true;
6581             }
6582         //trace("fullName:%s", fullName.c_str());
6583         FILE *f = fopen(fullName.c_str(), "w");
6584         if (!f)
6585             {
6586             error("<makefile> could not open %s for writing : %s",
6587                 fullName.c_str(), strerror(errno));
6588             return false;
6589             }
6590         for (unsigned int i=0 ; i<text.size() ; i++)
6591             fputc(text[i], f);
6592         fputc('\n', f);
6593         fclose(f);
6594         return true;
6595         }
6597     virtual bool parse(Element *elem)
6598         {
6599         if (!parent.getAttribute(elem, "file", fileName))
6600             return false;
6601         if (fileName.size() == 0)
6602             {
6603             error("<makefile> requires 'file=\"filename\"' attribute");
6604             return false;
6605             }
6606         if (!parent.getValue(elem, text))
6607             return false;
6608         text = leftJustify(text);
6609         //trace("dirname:%s", dirName.c_str());
6610         return true;
6611         }
6613 private:
6615     String fileName;
6616     String text;
6617 };
6621 /**
6622  * Create a named directory
6623  */
6624 class TaskMkDir : public Task
6626 public:
6628     TaskMkDir(MakeBase &par) : Task(par)
6629         { type = TASK_MKDIR; name = "mkdir"; }
6631     virtual ~TaskMkDir()
6632         {}
6634     virtual bool execute()
6635         {
6636         status("          : %s", dirName.c_str());
6637         String fullDir = parent.resolve(dirName);
6638         //trace("fullDir:%s", fullDir.c_str());
6639         if (!createDirectory(fullDir))
6640             return false;
6641         return true;
6642         }
6644     virtual bool parse(Element *elem)
6645         {
6646         if (!parent.getAttribute(elem, "dir", dirName))
6647             return false;
6648         if (dirName.size() == 0)
6649             {
6650             error("<mkdir> requires 'dir=\"dirname\"' attribute");
6651             return false;
6652             }
6653         return true;
6654         }
6656 private:
6658     String dirName;
6659 };
6663 /**
6664  * Create a named directory
6665  */
6666 class TaskMsgFmt: public Task
6668 public:
6670     TaskMsgFmt(MakeBase &par) : Task(par)
6671          {
6672          type    = TASK_MSGFMT;
6673          name    = "msgfmt";
6674          command = "msgfmt";
6675          owndir  = false;
6676          outName = "";
6677          }
6679     virtual ~TaskMsgFmt()
6680         {}
6682     virtual bool execute()
6683         {
6684         if (!listFiles(parent, fileSet))
6685             return false;
6686         String fileSetDir = fileSet.getDirectory();
6688         //trace("msgfmt: %d", fileSet.size());
6689         for (unsigned int i=0 ; i<fileSet.size() ; i++)
6690             {
6691             String fileName = fileSet[i];
6692             if (getSuffix(fileName) != "po")
6693                 continue;
6694             String sourcePath;
6695             if (fileSetDir.size()>0)
6696                 {
6697                 sourcePath.append(fileSetDir);
6698                 sourcePath.append("/");
6699                 }
6700             sourcePath.append(fileName);
6701             String fullSource = parent.resolve(sourcePath);
6703             String destPath;
6704             if (toDirName.size()>0)
6705                 {
6706                 destPath.append(toDirName);
6707                 destPath.append("/");
6708                 }
6709             if (owndir)
6710                 {
6711                 String subdir = fileName;
6712                 unsigned int pos = subdir.find_last_of('.');
6713                 if (pos != subdir.npos)
6714                     subdir = subdir.substr(0, pos);
6715                 destPath.append(subdir);
6716                 destPath.append("/");
6717                 }
6718             //Pick the output file name
6719             if (outName.size() > 0)
6720                 {
6721                 destPath.append(outName);
6722                 }
6723             else
6724                 {
6725                 destPath.append(fileName);
6726                 destPath[destPath.size()-2] = 'm';
6727                 }
6729             String fullDest = parent.resolve(destPath);
6731             if (!isNewerThan(fullSource, fullDest))
6732                 {
6733                 //trace("skip %s", fullSource.c_str());
6734                 continue;
6735                 }
6736                 
6737             String cmd = command;
6738             cmd.append(" ");
6739             cmd.append(fullSource);
6740             cmd.append(" -o ");
6741             cmd.append(fullDest);
6742             
6743             int pos = fullDest.find_last_of('/');
6744             if (pos>0)
6745                 {
6746                 String fullDestPath = fullDest.substr(0, pos);
6747                 if (!createDirectory(fullDestPath))
6748                     return false;
6749                 }
6753             String outString, errString;
6754             if (!executeCommand(cmd.c_str(), "", outString, errString))
6755                 {
6756                 error("<msgfmt> problem: %s", errString.c_str());
6757                 return false;
6758                 }
6759             }
6761         return true;
6762         }
6764     virtual bool parse(Element *elem)
6765         {
6766         String s;
6767         if (!parent.getAttribute(elem, "command", s))
6768             return false;
6769         if (s.size()>0)
6770             command = s;
6771         if (!parent.getAttribute(elem, "todir", toDirName))
6772             return false;
6773         if (!parent.getAttribute(elem, "out", outName))
6774             return false;
6775         if (!parent.getAttribute(elem, "owndir", s))
6776             return false;
6777         if (s.size()>0 && !getBool(s, owndir))
6778             return false;
6779             
6780         std::vector<Element *> children = elem->getChildren();
6781         for (unsigned int i=0 ; i<children.size() ; i++)
6782             {
6783             Element *child = children[i];
6784             String tagName = child->getName();
6785             if (tagName == "fileset")
6786                 {
6787                 if (!parseFileSet(child, parent, fileSet))
6788                     return false;
6789                 }
6790             }
6791         return true;
6792         }
6794 private:
6796     String  command;
6797     String  toDirName;
6798     String  outName;
6799     FileSet fileSet;
6800     bool    owndir;
6802 };
6808 /**
6809  *  Process an archive to allow random access
6810  */
6811 class TaskRanlib : public Task
6813 public:
6815     TaskRanlib(MakeBase &par) : Task(par)
6816         {
6817         type = TASK_RANLIB; name = "ranlib";
6818         command = "ranlib";
6819         }
6821     virtual ~TaskRanlib()
6822         {}
6824     virtual bool execute()
6825         {
6826         String fullName = parent.resolve(fileName);
6827         //trace("fullDir:%s", fullDir.c_str());
6828         String cmd = command;
6829         cmd.append(" ");
6830         cmd.append(fullName);
6831         String outbuf, errbuf;
6832         if (!executeCommand(cmd, "", outbuf, errbuf))
6833             return false;
6834         return true;
6835         }
6837     virtual bool parse(Element *elem)
6838         {
6839         String s;
6840         if (!parent.getAttribute(elem, "command", s))
6841             return false;
6842         if (s.size()>0)
6843            command = s;
6844         if (!parent.getAttribute(elem, "file", fileName))
6845             return false;
6846         if (fileName.size() == 0)
6847             {
6848             error("<ranlib> requires 'file=\"fileNname\"' attribute");
6849             return false;
6850             }
6851         return true;
6852         }
6854 private:
6856     String fileName;
6857     String command;
6858 };
6862 /**
6863  * Run the "ar" command to archive .o's into a .a
6864  */
6865 class TaskRC : public Task
6867 public:
6869     TaskRC(MakeBase &par) : Task(par)
6870         {
6871         type = TASK_RC; name = "rc";
6872         command = "windres";
6873         }
6875     virtual ~TaskRC()
6876         {}
6878     virtual bool execute()
6879         {
6880         String fullFile = parent.resolve(fileName);
6881         String fullOut  = parent.resolve(outName);
6882         if (!isNewerThan(fullFile, fullOut))
6883             return true;
6884         String cmd = command;
6885         cmd.append(" -o ");
6886         cmd.append(fullOut);
6887         cmd.append(" ");
6888         cmd.append(flags);
6889         cmd.append(" ");
6890         cmd.append(fullFile);
6892         String outString, errString;
6893         if (!executeCommand(cmd.c_str(), "", outString, errString))
6894             {
6895             error("RC problem: %s", errString.c_str());
6896             return false;
6897             }
6898         return true;
6899         }
6901     virtual bool parse(Element *elem)
6902         {
6903         if (!parent.getAttribute(elem, "command", command))
6904             return false;
6905         if (!parent.getAttribute(elem, "file", fileName))
6906             return false;
6907         if (!parent.getAttribute(elem, "out", outName))
6908             return false;
6909         std::vector<Element *> children = elem->getChildren();
6910         for (unsigned int i=0 ; i<children.size() ; i++)
6911             {
6912             Element *child = children[i];
6913             String tagName = child->getName();
6914             if (tagName == "flags")
6915                 {
6916                 if (!parent.getValue(child, flags))
6917                     return false;
6918                 }
6919             }
6920         return true;
6921         }
6923 private:
6925     String command;
6926     String flags;
6927     String fileName;
6928     String outName;
6930 };
6934 /**
6935  *  Collect .o's into a .so or DLL
6936  */
6937 class TaskSharedLib : public Task
6939 public:
6941     TaskSharedLib(MakeBase &par) : Task(par)
6942         {
6943         type = TASK_SHAREDLIB; name = "dll";
6944         command = "ar crv";
6945         }
6947     virtual ~TaskSharedLib()
6948         {}
6950     virtual bool execute()
6951         {
6952         //trace("###########HERE %d", fileSet.size());
6953         bool doit = false;
6954         
6955         String fullOut = parent.resolve(fileName);
6956         //trace("ar fullout: %s", fullOut.c_str());
6957         
6958         if (!listFiles(parent, fileSet))
6959             return false;
6960         String fileSetDir = fileSet.getDirectory();
6962         for (unsigned int i=0 ; i<fileSet.size() ; i++)
6963             {
6964             String fname;
6965             if (fileSetDir.size()>0)
6966                 {
6967                 fname.append(fileSetDir);
6968                 fname.append("/");
6969                 }
6970             fname.append(fileSet[i]);
6971             String fullName = parent.resolve(fname);
6972             //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
6973             if (isNewerThan(fullName, fullOut))
6974                 doit = true;
6975             }
6976         //trace("Needs it:%d", doit);
6977         if (!doit)
6978             {
6979             return true;
6980             }
6982         String cmd = "dllwrap";
6983         cmd.append(" -o ");
6984         cmd.append(fullOut);
6985         if (defFileName.size()>0)
6986             {
6987             cmd.append(" --def ");
6988             cmd.append(defFileName);
6989             cmd.append(" ");
6990             }
6991         if (impFileName.size()>0)
6992             {
6993             cmd.append(" --implib ");
6994             cmd.append(impFileName);
6995             cmd.append(" ");
6996             }
6997         for (unsigned int i=0 ; i<fileSet.size() ; i++)
6998             {
6999             String fname;
7000             if (fileSetDir.size()>0)
7001                 {
7002                 fname.append(fileSetDir);
7003                 fname.append("/");
7004                 }
7005             fname.append(fileSet[i]);
7006             String fullName = parent.resolve(fname);
7008             cmd.append(" ");
7009             cmd.append(fullName);
7010             }
7011         cmd.append(" ");
7012         cmd.append(libs);
7014         String outString, errString;
7015         if (!executeCommand(cmd.c_str(), "", outString, errString))
7016             {
7017             error("<sharedlib> problem: %s", errString.c_str());
7018             return false;
7019             }
7021         return true;
7022         }
7024     virtual bool parse(Element *elem)
7025         {
7026         if (!parent.getAttribute(elem, "file", fileName))
7027             return false;
7028         if (!parent.getAttribute(elem, "import", impFileName))
7029             return false;
7030         if (!parent.getAttribute(elem, "def", defFileName))
7031             return false;
7032             
7033         std::vector<Element *> children = elem->getChildren();
7034         for (unsigned int i=0 ; i<children.size() ; i++)
7035             {
7036             Element *child = children[i];
7037             String tagName = child->getName();
7038             if (tagName == "fileset")
7039                 {
7040                 if (!parseFileSet(child, parent, fileSet))
7041                     return false;
7042                 }
7043             else if (tagName == "libs")
7044                 {
7045                 if (!parent.getValue(child, libs))
7046                     return false;
7047                 libs = strip(libs);
7048                 }
7049             }
7050         return true;
7051         }
7053 private:
7055     String command;
7056     String fileName;
7057     String defFileName;
7058     String impFileName;
7059     FileSet fileSet;
7060     String libs;
7062 };
7066 /**
7067  * Run the "ar" command to archive .o's into a .a
7068  */
7069 class TaskStaticLib : public Task
7071 public:
7073     TaskStaticLib(MakeBase &par) : Task(par)
7074         {
7075         type = TASK_STATICLIB; name = "staticlib";
7076         command = "ar crv";
7077         }
7079     virtual ~TaskStaticLib()
7080         {}
7082     virtual bool execute()
7083         {
7084         //trace("###########HERE %d", fileSet.size());
7085         bool doit = false;
7086         
7087         String fullOut = parent.resolve(fileName);
7088         //trace("ar fullout: %s", fullOut.c_str());
7089         
7090         if (!listFiles(parent, fileSet))
7091             return false;
7092         String fileSetDir = fileSet.getDirectory();
7094         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7095             {
7096             String fname;
7097             if (fileSetDir.size()>0)
7098                 {
7099                 fname.append(fileSetDir);
7100                 fname.append("/");
7101                 }
7102             fname.append(fileSet[i]);
7103             String fullName = parent.resolve(fname);
7104             //trace("ar : %s/%s", fullOut.c_str(), fullName.c_str());
7105             if (isNewerThan(fullName, fullOut))
7106                 doit = true;
7107             }
7108         //trace("Needs it:%d", doit);
7109         if (!doit)
7110             {
7111             return true;
7112             }
7114         String cmd = command;
7115         cmd.append(" ");
7116         cmd.append(fullOut);
7117         for (unsigned int i=0 ; i<fileSet.size() ; i++)
7118             {
7119             String fname;
7120             if (fileSetDir.size()>0)
7121                 {
7122                 fname.append(fileSetDir);
7123                 fname.append("/");
7124                 }
7125             fname.append(fileSet[i]);
7126             String fullName = parent.resolve(fname);
7128             cmd.append(" ");
7129             cmd.append(fullName);
7130             }
7132         String outString, errString;
7133         if (!executeCommand(cmd.c_str(), "", outString, errString))
7134             {
7135             error("<staticlib> problem: %s", errString.c_str());
7136             return false;
7137             }
7139         return true;
7140         }
7143     virtual bool parse(Element *elem)
7144         {
7145         String s;
7146         if (!parent.getAttribute(elem, "command", s))
7147             return false;
7148         if (s.size()>0)
7149             command = s;
7150         if (!parent.getAttribute(elem, "file", fileName))
7151             return false;
7152             
7153         std::vector<Element *> children = elem->getChildren();
7154         for (unsigned int i=0 ; i<children.size() ; i++)
7155             {
7156             Element *child = children[i];
7157             String tagName = child->getName();
7158             if (tagName == "fileset")
7159                 {
7160                 if (!parseFileSet(child, parent, fileSet))
7161                     return false;
7162                 }
7163             }
7164         return true;
7165         }
7167 private:
7169     String command;
7170     String fileName;
7171     FileSet fileSet;
7173 };
7178 /**
7179  * Strip an executable
7180  */
7181 class TaskStrip : public Task
7183 public:
7185     TaskStrip(MakeBase &par) : Task(par)
7186         { type = TASK_STRIP; name = "strip"; }
7188     virtual ~TaskStrip()
7189         {}
7191     virtual bool execute()
7192         {
7193         String fullName = parent.resolve(fileName);
7194         //trace("fullDir:%s", fullDir.c_str());
7195         String cmd;
7196         String outbuf, errbuf;
7198         if (symFileName.size()>0)
7199             {
7200             String symFullName = parent.resolve(symFileName);
7201             cmd = "objcopy --only-keep-debug ";
7202             cmd.append(getNativePath(fullName));
7203             cmd.append(" ");
7204             cmd.append(getNativePath(symFullName));
7205             if (!executeCommand(cmd, "", outbuf, errbuf))
7206                 {
7207                 error("<strip> symbol file failed : %s", errbuf.c_str());
7208                 return false;
7209                 }
7210             }
7211             
7212         cmd = "strip ";
7213         cmd.append(getNativePath(fullName));
7214         if (!executeCommand(cmd, "", outbuf, errbuf))
7215             {
7216             error("<strip> failed : %s", errbuf.c_str());
7217             return false;
7218             }
7219         return true;
7220         }
7222     virtual bool parse(Element *elem)
7223         {
7224         if (!parent.getAttribute(elem, "file", fileName))
7225             return false;
7226         if (!parent.getAttribute(elem, "symfile", symFileName))
7227             return false;
7228         if (fileName.size() == 0)
7229             {
7230             error("<strip> requires 'file=\"fileName\"' attribute");
7231             return false;
7232             }
7233         return true;
7234         }
7236 private:
7238     String fileName;
7239     String symFileName;
7240 };
7243 /**
7244  *
7245  */
7246 class TaskTouch : public Task
7248 public:
7250     TaskTouch(MakeBase &par) : Task(par)
7251         { type = TASK_TOUCH; name = "touch"; }
7253     virtual ~TaskTouch()
7254         {}
7256     virtual bool execute()
7257         {
7258         String fullName = parent.resolve(fileName);
7259         String nativeFile = getNativePath(fullName);
7260         if (!isRegularFile(fullName) && !isDirectory(fullName))
7261             {            
7262             // S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH
7263             int ret = creat(nativeFile.c_str(), 0666);
7264             if (ret != 0) 
7265                 {
7266                 error("<touch> could not create '%s' : %s",
7267                     nativeFile.c_str(), strerror(ret));
7268                 return false;
7269                 }
7270             return true;
7271             }
7272         int ret = utime(nativeFile.c_str(), (struct utimbuf *)0);
7273         if (ret != 0)
7274             {
7275             error("<touch> could not update the modification time for '%s' : %s",
7276                 nativeFile.c_str(), strerror(ret));
7277             return false;
7278             }
7279         return true;
7280         }
7282     virtual bool parse(Element *elem)
7283         {
7284         //trace("touch parse");
7285         if (!parent.getAttribute(elem, "file", fileName))
7286             return false;
7287         if (fileName.size() == 0)
7288             {
7289             error("<touch> requires 'file=\"fileName\"' attribute");
7290             return false;
7291             }
7292         return true;
7293         }
7295     String fileName;
7296 };
7299 /**
7300  *
7301  */
7302 class TaskTstamp : public Task
7304 public:
7306     TaskTstamp(MakeBase &par) : Task(par)
7307         { type = TASK_TSTAMP; name = "tstamp"; }
7309     virtual ~TaskTstamp()
7310         {}
7312     virtual bool execute()
7313         {
7314         return true;
7315         }
7317     virtual bool parse(Element *elem)
7318         {
7319         //trace("tstamp parse");
7320         return true;
7321         }
7322 };
7326 /**
7327  *
7328  */
7329 Task *Task::createTask(Element *elem)
7331     String tagName = elem->getName();
7332     //trace("task:%s", tagName.c_str());
7333     Task *task = NULL;
7334     if (tagName == "cc")
7335         task = new TaskCC(parent);
7336     else if (tagName == "copy")
7337         task = new TaskCopy(parent);
7338     else if (tagName == "delete")
7339         task = new TaskDelete(parent);
7340     else if (tagName == "jar")
7341         task = new TaskJar(parent);
7342     else if (tagName == "javac")
7343         task = new TaskJavac(parent);
7344     else if (tagName == "link")
7345         task = new TaskLink(parent);
7346     else if (tagName == "makefile")
7347         task = new TaskMakeFile(parent);
7348     else if (tagName == "mkdir")
7349         task = new TaskMkDir(parent);
7350     else if (tagName == "msgfmt")
7351         task = new TaskMsgFmt(parent);
7352     else if (tagName == "ranlib")
7353         task = new TaskRanlib(parent);
7354     else if (tagName == "rc")
7355         task = new TaskRC(parent);
7356     else if (tagName == "sharedlib")
7357         task = new TaskSharedLib(parent);
7358     else if (tagName == "staticlib")
7359         task = new TaskStaticLib(parent);
7360     else if (tagName == "strip")
7361         task = new TaskStrip(parent);
7362     else if (tagName == "touch")
7363         task = new TaskTouch(parent);
7364     else if (tagName == "tstamp")
7365         task = new TaskTstamp(parent);
7366     else
7367         {
7368         error("Unknown task '%s'", tagName.c_str());
7369         return NULL;
7370         }
7372     if (!task->parse(elem))
7373         {
7374         delete task;
7375         return NULL;
7376         }
7377     return task;
7382 //########################################################################
7383 //# T A R G E T
7384 //########################################################################
7386 /**
7387  *
7388  */
7389 class Target : public MakeBase
7392 public:
7394     /**
7395      *
7396      */
7397     Target(Make &par) : parent(par)
7398         { init(); }
7400     /**
7401      *
7402      */
7403     Target(const Target &other) : parent(other.parent)
7404         { init(); assign(other); }
7406     /**
7407      *
7408      */
7409     Target &operator=(const Target &other)
7410         { init(); assign(other); return *this; }
7412     /**
7413      *
7414      */
7415     virtual ~Target()
7416         { cleanup() ; }
7419     /**
7420      *
7421      */
7422     virtual Make &getParent()
7423         { return parent; }
7425     /**
7426      *
7427      */
7428     virtual String getName()
7429         { return name; }
7431     /**
7432      *
7433      */
7434     virtual void setName(const String &val)
7435         { name = val; }
7437     /**
7438      *
7439      */
7440     virtual String getDescription()
7441         { return description; }
7443     /**
7444      *
7445      */
7446     virtual void setDescription(const String &val)
7447         { description = val; }
7449     /**
7450      *
7451      */
7452     virtual void addDependency(const String &val)
7453         { deps.push_back(val); }
7455     /**
7456      *
7457      */
7458     virtual void parseDependencies(const String &val)
7459         { deps = tokenize(val, ", "); }
7461     /**
7462      *
7463      */
7464     virtual std::vector<String> &getDependencies()
7465         { return deps; }
7467     /**
7468      *
7469      */
7470     virtual String getIf()
7471         { return ifVar; }
7473     /**
7474      *
7475      */
7476     virtual void setIf(const String &val)
7477         { ifVar = val; }
7479     /**
7480      *
7481      */
7482     virtual String getUnless()
7483         { return unlessVar; }
7485     /**
7486      *
7487      */
7488     virtual void setUnless(const String &val)
7489         { unlessVar = val; }
7491     /**
7492      *
7493      */
7494     virtual void addTask(Task *val)
7495         { tasks.push_back(val); }
7497     /**
7498      *
7499      */
7500     virtual std::vector<Task *> &getTasks()
7501         { return tasks; }
7503 private:
7505     void init()
7506         {
7507         }
7509     void cleanup()
7510         {
7511         tasks.clear();
7512         }
7514     void assign(const Target &other)
7515         {
7516         //parent      = other.parent;
7517         name        = other.name;
7518         description = other.description;
7519         ifVar       = other.ifVar;
7520         unlessVar   = other.unlessVar;
7521         deps        = other.deps;
7522         tasks       = other.tasks;
7523         }
7525     Make &parent;
7527     String name;
7529     String description;
7531     String ifVar;
7533     String unlessVar;
7535     std::vector<String> deps;
7537     std::vector<Task *> tasks;
7539 };
7548 //########################################################################
7549 //# M A K E
7550 //########################################################################
7553 /**
7554  *
7555  */
7556 class Make : public MakeBase
7559 public:
7561     /**
7562      *
7563      */
7564     Make()
7565         { init(); }
7567     /**
7568      *
7569      */
7570     Make(const Make &other)
7571         { assign(other); }
7573     /**
7574      *
7575      */
7576     Make &operator=(const Make &other)
7577         { assign(other); return *this; }
7579     /**
7580      *
7581      */
7582     virtual ~Make()
7583         { cleanup(); }
7585     /**
7586      *
7587      */
7588     virtual std::map<String, Target> &getTargets()
7589         { return targets; }
7592     /**
7593      *
7594      */
7595     virtual String version()
7596         { return BUILDTOOL_VERSION; }
7598     /**
7599      * Overload a <property>
7600      */
7601     virtual bool specifyProperty(const String &name,
7602                                  const String &value);
7604     /**
7605      *
7606      */
7607     virtual bool run();
7609     /**
7610      *
7611      */
7612     virtual bool run(const String &target);
7616 private:
7618     /**
7619      *
7620      */
7621     void init();
7623     /**
7624      *
7625      */
7626     void cleanup();
7628     /**
7629      *
7630      */
7631     void assign(const Make &other);
7633     /**
7634      *
7635      */
7636     bool executeTask(Task &task);
7639     /**
7640      *
7641      */
7642     bool executeTarget(Target &target,
7643              std::set<String> &targetsCompleted);
7646     /**
7647      *
7648      */
7649     bool execute();
7651     /**
7652      *
7653      */
7654     bool checkTargetDependencies(Target &prop,
7655                     std::vector<String> &depList);
7657     /**
7658      *
7659      */
7660     bool parsePropertyFile(const String &fileName,
7661                            const String &prefix);
7663     /**
7664      *
7665      */
7666     bool parseProperty(Element *elem);
7668     /**
7669      *
7670      */
7671     bool parseTask(Task &task, Element *elem);
7673     /**
7674      *
7675      */
7676     bool parseFile();
7678     /**
7679      *
7680      */
7681     std::vector<String> glob(const String &pattern);
7684     //###############
7685     //# Fields
7686     //###############
7688     String projectName;
7690     String currentTarget;
7692     String defaultTarget;
7694     String specifiedTarget;
7696     String baseDir;
7698     String description;
7699     
7700     String envAlias;
7702     //std::vector<Property> properties;
7703     
7704     std::map<String, Target> targets;
7706     std::vector<Task *> allTasks;
7707     
7708     std::map<String, String> specifiedProperties;
7710 };
7713 //########################################################################
7714 //# C L A S S  M A I N T E N A N C E
7715 //########################################################################
7717 /**
7718  *
7719  */
7720 void Make::init()
7722     uri             = "build.xml";
7723     projectName     = "";
7724     currentTarget   = "";
7725     defaultTarget   = "";
7726     specifiedTarget = "";
7727     baseDir         = "";
7728     description     = "";
7729     envAlias        = "";
7730     properties.clear();
7731     for (unsigned int i = 0 ; i < allTasks.size() ; i++)
7732         delete allTasks[i];
7733     allTasks.clear();
7738 /**
7739  *
7740  */
7741 void Make::cleanup()
7743     for (unsigned int i = 0 ; i < allTasks.size() ; i++)
7744         delete allTasks[i];
7745     allTasks.clear();
7750 /**
7751  *
7752  */
7753 void Make::assign(const Make &other)
7755     uri              = other.uri;
7756     projectName      = other.projectName;
7757     currentTarget    = other.currentTarget;
7758     defaultTarget    = other.defaultTarget;
7759     specifiedTarget  = other.specifiedTarget;
7760     baseDir          = other.baseDir;
7761     description      = other.description;
7762     properties       = other.properties;
7767 //########################################################################
7768 //# U T I L I T Y    T A S K S
7769 //########################################################################
7771 /**
7772  *  Perform a file globbing
7773  */
7774 std::vector<String> Make::glob(const String &pattern)
7776     std::vector<String> res;
7777     return res;
7781 //########################################################################
7782 //# P U B L I C    A P I
7783 //########################################################################
7787 /**
7788  *
7789  */
7790 bool Make::executeTarget(Target &target,
7791              std::set<String> &targetsCompleted)
7794     String name = target.getName();
7796     //First get any dependencies for this target
7797     std::vector<String> deps = target.getDependencies();
7798     for (unsigned int i=0 ; i<deps.size() ; i++)
7799         {
7800         String dep = deps[i];
7801         //Did we do it already?  Skip
7802         if (targetsCompleted.find(dep)!=targetsCompleted.end())
7803             continue;
7804             
7805         std::map<String, Target> &tgts =
7806                target.getParent().getTargets();
7807         std::map<String, Target>::iterator iter =
7808                tgts.find(dep);
7809         if (iter == tgts.end())
7810             {
7811             error("Target '%s' dependency '%s' not found",
7812                       name.c_str(),  dep.c_str());
7813             return false;
7814             }
7815         Target depTarget = iter->second;
7816         if (!executeTarget(depTarget, targetsCompleted))
7817             {
7818             return false;
7819             }
7820         }
7822     status("## Target : %s", name.c_str());
7824     //Now let's do the tasks
7825     std::vector<Task *> &tasks = target.getTasks();
7826     for (unsigned int i=0 ; i<tasks.size() ; i++)
7827         {
7828         Task *task = tasks[i];
7829         status("---- task : %s", task->getName().c_str());
7830         if (!task->execute())
7831             {
7832             return false;
7833             }
7834         }
7835         
7836     targetsCompleted.insert(name);
7837     
7838     return true;
7843 /**
7844  *  Main execute() method.  Start here and work
7845  *  up the dependency tree 
7846  */
7847 bool Make::execute()
7849     status("######## EXECUTE");
7851     //Determine initial target
7852     if (specifiedTarget.size()>0)
7853         {
7854         currentTarget = specifiedTarget;
7855         }
7856     else if (defaultTarget.size()>0)
7857         {
7858         currentTarget = defaultTarget;
7859         }
7860     else
7861         {
7862         error("execute: no specified or default target requested");
7863         return false;
7864         }
7866     std::map<String, Target>::iterator iter =
7867                targets.find(currentTarget);
7868     if (iter == targets.end())
7869         {
7870         error("Initial target '%s' not found",
7871                  currentTarget.c_str());
7872         return false;
7873         }
7874         
7875     //Now run
7876     Target target = iter->second;
7877     std::set<String> targetsCompleted;
7878     if (!executeTarget(target, targetsCompleted))
7879         {
7880         return false;
7881         }
7883     status("######## EXECUTE COMPLETE");
7884     return true;
7890 /**
7891  *
7892  */
7893 bool Make::checkTargetDependencies(Target &target, 
7894                             std::vector<String> &depList)
7896     String tgtName = target.getName().c_str();
7897     depList.push_back(tgtName);
7899     std::vector<String> deps = target.getDependencies();
7900     for (unsigned int i=0 ; i<deps.size() ; i++)
7901         {
7902         String dep = deps[i];
7903         //First thing entered was the starting Target
7904         if (dep == depList[0])
7905             {
7906             error("Circular dependency '%s' found at '%s'",
7907                       dep.c_str(), tgtName.c_str());
7908             std::vector<String>::iterator diter;
7909             for (diter=depList.begin() ; diter!=depList.end() ; diter++)
7910                 {
7911                 error("  %s", diter->c_str());
7912                 }
7913             return false;
7914             }
7916         std::map<String, Target> &tgts =
7917                   target.getParent().getTargets();
7918         std::map<String, Target>::iterator titer = tgts.find(dep);
7919         if (titer == tgts.end())
7920             {
7921             error("Target '%s' dependency '%s' not found",
7922                       tgtName.c_str(), dep.c_str());
7923             return false;
7924             }
7925         if (!checkTargetDependencies(titer->second, depList))
7926             {
7927             return false;
7928             }
7929         }
7930     return true;
7937 static int getword(int pos, const String &inbuf, String &result)
7939     int p = pos;
7940     int len = (int)inbuf.size();
7941     String val;
7942     while (p < len)
7943         {
7944         char ch = inbuf[p];
7945         if (!isalnum(ch) && ch!='.' && ch!='_')
7946             break;
7947         val.push_back(ch);
7948         p++;
7949         }
7950     result = val;
7951     return p;
7957 /**
7958  *
7959  */
7960 bool Make::parsePropertyFile(const String &fileName,
7961                              const String &prefix)
7963     FILE *f = fopen(fileName.c_str(), "r");
7964     if (!f)
7965         {
7966         error("could not open property file %s", fileName.c_str());
7967         return false;
7968         }
7969     int linenr = 0;
7970     while (!feof(f))
7971         {
7972         char buf[256];
7973         if (!fgets(buf, 255, f))
7974             break;
7975         linenr++;
7976         String s = buf;
7977         s = trim(s);
7978         int len = s.size();
7979         if (len == 0)
7980             continue;
7981         if (s[0] == '#')
7982             continue;
7983         String key;
7984         String val;
7985         int p = 0;
7986         int p2 = getword(p, s, key);
7987         if (p2 <= p)
7988             {
7989             error("property file %s, line %d: expected keyword",
7990                     fileName.c_str(), linenr);
7991             return false;
7992             }
7993         if (prefix.size() > 0)
7994             {
7995             key.insert(0, prefix);
7996             }
7998         //skip whitespace
7999         for (p=p2 ; p<len ; p++)
8000             if (!isspace(s[p]))
8001                 break;
8003         if (p>=len || s[p]!='=')
8004             {
8005             error("property file %s, line %d: expected '='",
8006                     fileName.c_str(), linenr);
8007             return false;
8008             }
8009         p++;
8011         //skip whitespace
8012         for ( ; p<len ; p++)
8013             if (!isspace(s[p]))
8014                 break;
8016         /* This way expects a word after the =
8017         p2 = getword(p, s, val);
8018         if (p2 <= p)
8019             {
8020             error("property file %s, line %d: expected value",
8021                     fileName.c_str(), linenr);
8022             return false;
8023             }
8024         */
8025         // This way gets the rest of the line after the =
8026         if (p>=len)
8027             {
8028             error("property file %s, line %d: expected value",
8029                     fileName.c_str(), linenr);
8030             return false;
8031             }
8032         val = s.substr(p);
8033         if (key.size()==0)
8034             continue;
8035         //allow property to be set, even if val=""
8037         //trace("key:'%s' val:'%s'", key.c_str(), val.c_str());
8038         //See if we wanted to overload this property
8039         std::map<String, String>::iterator iter =
8040             specifiedProperties.find(key);
8041         if (iter!=specifiedProperties.end())
8042             {
8043             val = iter->second;
8044             status("overloading property '%s' = '%s'",
8045                    key.c_str(), val.c_str());
8046             }
8047         properties[key] = val;
8048         }
8049     fclose(f);
8050     return true;
8056 /**
8057  *
8058  */
8059 bool Make::parseProperty(Element *elem)
8061     std::vector<Attribute> &attrs = elem->getAttributes();
8062     for (unsigned int i=0 ; i<attrs.size() ; i++)
8063         {
8064         String attrName = attrs[i].getName();
8065         String attrVal  = attrs[i].getValue();
8067         if (attrName == "name")
8068             {
8069             String val;
8070             if (!getAttribute(elem, "value", val))
8071                 return false;
8072             if (val.size() > 0)
8073                 {
8074                 properties[attrVal] = val;
8075                 }
8076             else
8077                 {
8078                 if (!getAttribute(elem, "location", val))
8079                     return false;
8080                 //let the property exist, even if not defined
8081                 properties[attrVal] = val;
8082                 }
8083             //See if we wanted to overload this property
8084             std::map<String, String>::iterator iter =
8085                 specifiedProperties.find(attrVal);
8086             if (iter != specifiedProperties.end())
8087                 {
8088                 val = iter->second;
8089                 status("overloading property '%s' = '%s'",
8090                     attrVal.c_str(), val.c_str());
8091                 properties[attrVal] = val;
8092                 }
8093             }
8094         else if (attrName == "file")
8095             {
8096             String prefix;
8097             if (!getAttribute(elem, "prefix", prefix))
8098                 return false;
8099             if (prefix.size() > 0)
8100                 {
8101                 if (prefix[prefix.size()-1] != '.')
8102                     prefix.push_back('.');
8103                 }
8104             if (!parsePropertyFile(attrName, prefix))
8105                 return false;
8106             }
8107         else if (attrName == "environment")
8108             {
8109             if (envAlias.size() > 0)
8110                 {
8111                 error("environment property can only be set once");
8112                 return false;
8113                 }
8114             envAlias = attrVal;
8115             }
8116         }
8118     return true;
8124 /**
8125  *
8126  */
8127 bool Make::parseFile()
8129     status("######## PARSE : %s", uri.getPath().c_str());
8131     Parser parser;
8132     Element *root = parser.parseFile(uri.getNativePath());
8133     if (!root)
8134         {
8135         error("Could not open %s for reading",
8136               uri.getNativePath().c_str());
8137         return false;
8138         }
8140     if (root->getChildren().size()==0 ||
8141         root->getChildren()[0]->getName()!="project")
8142         {
8143         error("Main xml element should be <project>");
8144         delete root;
8145         return false;
8146         }
8148     //########## Project attributes
8149     Element *project = root->getChildren()[0];
8150     String s = project->getAttribute("name");
8151     if (s.size() > 0)
8152         projectName = s;
8153     s = project->getAttribute("default");
8154     if (s.size() > 0)
8155         defaultTarget = s;
8156     s = project->getAttribute("basedir");
8157     if (s.size() > 0)
8158         baseDir = s;
8160     //######### PARSE MEMBERS
8161     std::vector<Element *> children = project->getChildren();
8162     for (unsigned int i=0 ; i<children.size() ; i++)
8163         {
8164         Element *elem = children[i];
8165         String tagName = elem->getName();
8167         //########## DESCRIPTION
8168         if (tagName == "description")
8169             {
8170             description = parser.trim(elem->getValue());
8171             }
8173         //######### PROPERTY
8174         else if (tagName == "property")
8175             {
8176             if (!parseProperty(elem))
8177                 return false;
8178             }
8180         //######### TARGET
8181         else if (tagName == "target")
8182             {
8183             String tname   = elem->getAttribute("name");
8184             String tdesc   = elem->getAttribute("description");
8185             String tdeps   = elem->getAttribute("depends");
8186             String tif     = elem->getAttribute("if");
8187             String tunless = elem->getAttribute("unless");
8188             Target target(*this);
8189             target.setName(tname);
8190             target.setDescription(tdesc);
8191             target.parseDependencies(tdeps);
8192             target.setIf(tif);
8193             target.setUnless(tunless);
8194             std::vector<Element *> telems = elem->getChildren();
8195             for (unsigned int i=0 ; i<telems.size() ; i++)
8196                 {
8197                 Element *telem = telems[i];
8198                 Task breeder(*this);
8199                 Task *task = breeder.createTask(telem);
8200                 if (!task)
8201                     return false;
8202                 allTasks.push_back(task);
8203                 target.addTask(task);
8204                 }
8206             //Check name
8207             if (tname.size() == 0)
8208                 {
8209                 error("no name for target");
8210                 return false;
8211                 }
8212             //Check for duplicate name
8213             if (targets.find(tname) != targets.end())
8214                 {
8215                 error("target '%s' already defined", tname.c_str());
8216                 return false;
8217                 }
8218             //more work than targets[tname]=target, but avoids default allocator
8219             targets.insert(std::make_pair<String, Target>(tname, target));
8220             }
8222         }
8224     std::map<String, Target>::iterator iter;
8225     for (iter = targets.begin() ; iter!= targets.end() ; iter++)
8226         {
8227         Target tgt = iter->second;
8228         std::vector<String> depList;
8229         if (!checkTargetDependencies(tgt, depList))
8230             {
8231             return false;
8232             }
8233         }
8236     delete root;
8237     status("######## PARSE COMPLETE");
8238     return true;
8242 /**
8243  * Overload a <property>
8244  */
8245 bool Make::specifyProperty(const String &name, const String &value)
8247     if (specifiedProperties.find(name) != specifiedProperties.end())
8248         {
8249         error("Property %s already specified", name.c_str());
8250         return false;
8251         }
8252     specifiedProperties[name] = value;
8253     return true;
8258 /**
8259  *
8260  */
8261 bool Make::run()
8263     if (!parseFile())
8264         return false;
8265         
8266     if (!execute())
8267         return false;
8269     return true;
8275 /**
8276  * Get a formatted MM:SS.sss time elapsed string
8277  */ 
8278 static String
8279 timeDiffString(struct timeval &x, struct timeval &y)
8281     long microsX  = x.tv_usec;
8282     long secondsX = x.tv_sec;
8283     long microsY  = y.tv_usec;
8284     long secondsY = y.tv_sec;
8285     if (microsX < microsY)
8286         {
8287         microsX += 1000000;
8288         secondsX -= 1;
8289         }
8291     int seconds = (int)(secondsX - secondsY);
8292     int millis  = (int)((microsX - microsY)/1000);
8294     int minutes = seconds/60;
8295     seconds -= minutes*60;
8296     char buf[80];
8297     snprintf(buf, 79, "%dm %d.%03ds", minutes, seconds, millis);
8298     String ret = buf;
8299     return ret;
8300     
8303 /**
8304  *
8305  */
8306 bool Make::run(const String &target)
8308     status("####################################################");
8309     status("#   %s", version().c_str());
8310     status("####################################################");
8311     struct timeval timeStart, timeEnd;
8312     ::gettimeofday(&timeStart, NULL);
8313     specifiedTarget = target;
8314     if (!run())
8315         return false;
8316     ::gettimeofday(&timeEnd, NULL);
8317     String timeStr = timeDiffString(timeEnd, timeStart);
8318     status("####################################################");
8319     status("#   BuildTool Completed : %s", timeStr.c_str());
8320     status("####################################################");
8321     return true;
8330 }// namespace buildtool
8331 //########################################################################
8332 //# M A I N
8333 //########################################################################
8335 typedef buildtool::String String;
8337 /**
8338  *  Format an error message in printf() style
8339  */
8340 static void error(char *fmt, ...)
8342     va_list ap;
8343     va_start(ap, fmt);
8344     fprintf(stderr, "BuildTool error: ");
8345     vfprintf(stderr, fmt, ap);
8346     fprintf(stderr, "\n");
8347     va_end(ap);
8351 static bool parseProperty(const String &s, String &name, String &val)
8353     int len = s.size();
8354     int i;
8355     for (i=0 ; i<len ; i++)
8356         {
8357         char ch = s[i];
8358         if (ch == '=')
8359             break;
8360         name.push_back(ch);
8361         }
8362     if (i>=len || s[i]!='=')
8363         {
8364         error("property requires -Dname=value");
8365         return false;
8366         }
8367     i++;
8368     for ( ; i<len ; i++)
8369         {
8370         char ch = s[i];
8371         val.push_back(ch);
8372         }
8373     return true;
8377 /**
8378  * Compare a buffer with a key, for the length of the key
8379  */
8380 static bool sequ(const String &buf, char *key)
8382     int len = buf.size();
8383     for (int i=0 ; key[i] && i<len ; i++)
8384         {
8385         if (key[i] != buf[i])
8386             return false;
8387         }        
8388     return true;
8391 static void usage(int argc, char **argv)
8393     printf("usage:\n");
8394     printf("   %s [options] [target]\n", argv[0]);
8395     printf("Options:\n");
8396     printf("  -help, -h              print this message\n");
8397     printf("  -version               print the version information and exit\n");
8398     printf("  -file <file>           use given buildfile\n");
8399     printf("  -f <file>                 ''\n");
8400     printf("  -D<property>=<value>   use value for given property\n");
8406 /**
8407  * Parse the command-line args, get our options,
8408  * and run this thing
8409  */   
8410 static bool parseOptions(int argc, char **argv)
8412     if (argc < 1)
8413         {
8414         error("Cannot parse arguments");
8415         return false;
8416         }
8418     buildtool::Make make;
8420     String target;
8422     //char *progName = argv[0];
8423     for (int i=1 ; i<argc ; i++)
8424         {
8425         String arg = argv[i];
8426         if (arg.size()>1 && arg[0]=='-')
8427             {
8428             if (arg == "-h" || arg == "-help")
8429                 {
8430                 usage(argc,argv);
8431                 return true;
8432                 }
8433             else if (arg == "-version")
8434                 {
8435                 printf("%s", make.version().c_str());
8436                 return true;
8437                 }
8438             else if (arg == "-f" || arg == "-file")
8439                 {
8440                 if (i>=argc)
8441                    {
8442                    usage(argc, argv);
8443                    return false;
8444                    }
8445                 i++; //eat option
8446                 make.setURI(argv[i]);
8447                 }
8448             else if (arg.size()>2 && sequ(arg, "-D"))
8449                 {
8450                 String s = arg.substr(2, s.size());
8451                 String name, value;
8452                 if (!parseProperty(s, name, value))
8453                    {
8454                    usage(argc, argv);
8455                    return false;
8456                    }
8457                 if (!make.specifyProperty(name, value))
8458                     return false;
8459                 }
8460             else
8461                 {
8462                 error("Unknown option:%s", arg.c_str());
8463                 return false;
8464                 }
8465             }
8466         else
8467             {
8468             if (target.size()>0)
8469                 {
8470                 error("only one initial target");
8471                 usage(argc, argv);
8472                 return false;
8473                 }
8474             target = arg;
8475             }
8476         }
8478     //We have the options.  Now execute them
8479     if (!make.run(target))
8480         return false;
8482     return true;
8488 /*
8489 static bool runMake()
8491     buildtool::Make make;
8492     if (!make.run())
8493         return false;
8494     return true;
8498 static bool pkgConfigTest()
8500     buildtool::PkgConfig pkgConfig;
8501     if (!pkgConfig.readFile("gtk+-2.0.pc"))
8502         return false;
8503     return true;
8508 static bool depTest()
8510     buildtool::DepTool deptool;
8511     deptool.setSourceDirectory("/dev/ink/inkscape/src");
8512     if (!deptool.generateDependencies("build.dep"))
8513         return false;
8514     std::vector<buildtool::DepRec> res =
8515            deptool.loadDepFile("build.dep");
8516     if (res.size() == 0)
8517         return false;
8518     return true;
8521 static bool popenTest()
8523     buildtool::Make make;
8524     buildtool::String out, err;
8525     bool ret = make.executeCommand("gcc xx.cpp", "", out, err);
8526     printf("Popen test:%d '%s' '%s'\n", ret, out.c_str(), err.c_str());
8527     return true;
8531 static bool propFileTest()
8533     buildtool::Make make;
8534     make.parsePropertyFile("test.prop", "test.");
8535     return true;
8537 */
8539 int main(int argc, char **argv)
8542     if (!parseOptions(argc, argv))
8543         return 1;
8544     /*
8545     if (!popenTest())
8546         return 1;
8548     if (!depTest())
8549         return 1;
8550     if (!propFileTest())
8551         return 1;
8552     if (runMake())
8553         return 1;
8554     */
8555     return 0;
8559 //########################################################################
8560 //# E N D 
8561 //########################################################################